<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Netty16# 池化內(nèi)存Subpage類型內(nèi)存分配

          共 7268字,需瀏覽 15分鐘

           ·

          2021-04-13 08:09

          前言

          前面聊了大于8KB的內(nèi)存分配,那小于8KB的呢?上一篇的平衡二叉樹第十一層的葉子節(jié)點(diǎn)最小也是8KB,那比如要分配128B的緩存,直接分給8KB顯然是不合適的,Tiny是小于512Byte,Small介于512B~8KB,Tiny和Small統(tǒng)稱Subpage,本文就聊聊他們的內(nèi)存分配情況,這塊應(yīng)該是整個(gè)netty最為復(fù)雜的部分了。

          內(nèi)容提要

          下面是以分配128B為例的整體流程架構(gòu)圖,下面大體敘述下其流程。

          • 先從平衡二叉樹的第11層選一個(gè)未分配的葉子節(jié)點(diǎn)大小為8KB的一個(gè)Page
            備注:本例中為memoryMap[2048]

          • 對該P(yáng)age進(jìn)行切割,假如要分配128B,整體會切割為64塊
            備注:8192/128=64

          • 通過long類型二進(jìn)制64位來標(biāo)記分割成各個(gè)塊的分配狀態(tài)

            備注:0:未分配,1:已分配

          • 一個(gè)bitmap數(shù)組長度為8,每個(gè)元素都能對64塊內(nèi)存進(jìn)行標(biāo)記

          • 建立了二叉樹節(jié)點(diǎn)與切分塊之間的映射關(guān)系
            備注:memoryMapIdx ^ maxSubpageAllocs

          • 分配后建立二叉樹葉子節(jié)點(diǎn)與標(biāo)記位之間的關(guān)系,可以指向內(nèi)存一塊區(qū)域
            備注:0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx

          源碼分析

          示例代碼

          @Test
          public void testAllocateSubpage() {
              ByteBufAllocator allocator = new PooledByteBufAllocator();
              allocator.directBuffer(128); 
          }

          備注:以分配128B的內(nèi)存為例,分析其分配過程。

          源碼分析

          private long allocateSubpage(int normCapacity) {
              PoolSubpage<T> head = arena.findSubpagePoolHead(normCapacity); // 注解@1
             int d = maxOrder; 
              synchronized (head) {
                  int id = allocateNode(d); // 注解@2
               if (id < 0) {
                      return id;
                  }
                  final PoolSubpage<T>[] subpages = this.subpages; // 注解@3
                  final int pageSize = this.pageSize;
                  freeBytes -= pageSize;
                  int subpageIdx = subpageIdx(id); // 注解@4

                  PoolSubpage<T> subpage = subpages[subpageIdx]; 
                  if (subpage == null) { // 注解@5
                      subpage = new PoolSubpage<T>(head, this, id, runOffset(id), pageSize, normCapacity);
                      subpages[subpageIdx] = subpage;
                  } else {
                      subpage.init(head, normCapacity);
                  }
                  return subpage.allocate(); // 注解@6
              }
          }

          注解@1 從tinySubpagePools中獲取PoolSubpage。獲取過程為elemSize >>> 4(除以16)來獲取。

          tinySubpagePools結(jié)構(gòu)

          tinySubpagePools被初始化成長度為32的數(shù)組,元素之間差額為16B。

          注解@2 allocateNode 在上一篇文章分析過,d = maxOrder = 1。表示在平衡二叉樹的第11層找到可分配的節(jié)點(diǎn),具體為memoryMap數(shù)組中的下標(biāo)。如果整個(gè)樹都沒有內(nèi)存可分配了,返回的id=-1。

          注解@3 先看下subpages的初始化,maxSubpageAllocs = 1 << maxOrder= 2048。也就是PoolSubpage[] subpages的長度為平衡二叉樹第11層所有的節(jié)點(diǎn)數(shù)(2^11)。

          subpages = newSubpageArray(maxSubpageAllocs);

          注解@4 將平衡二叉樹第11層的下標(biāo)memoryMap[]的下標(biāo)轉(zhuǎn)換為subpages[]數(shù)組的下標(biāo)。轉(zhuǎn)換關(guān)系為memoryMapIdx ^ maxSubpageAllocs。

          例如:平衡二叉樹第11層第1個(gè)節(jié)點(diǎn)數(shù)組下標(biāo)為2048,轉(zhuǎn)換為subpages的下標(biāo)為0,平衡二叉樹第11層第2個(gè)節(jié)點(diǎn)數(shù)組下標(biāo)為2049,轉(zhuǎn)換為subpages的下標(biāo)為1,平衡二叉樹第11層第2個(gè)節(jié)點(diǎn)數(shù)組下標(biāo)為2050,轉(zhuǎn)換為subpages的下標(biāo)為2。

          注解@5 初始化PoolSubpage

          PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) {
            this.chunk = chunk;
            this.memoryMapIdx = memoryMapIdx;
            this.runOffset = runOffset;
            this.pageSize = pageSize;
            bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64
            init(head, elemSize);
          }

          參數(shù)說明

          head: PoolSubpage數(shù)組中的一個(gè)元素,本例中為第4個(gè)元素

          chunk: 當(dāng)前PoolChunk實(shí)例

          memoryMapIdx: 平衡二叉樹第11層用于分配的節(jié)點(diǎn),具體為memoryMap數(shù)組下標(biāo)

          elemSize: 待分配的內(nèi)存,本例中為128KB

          bitmap: long數(shù)組長度為8「8192無符號右移10位=8」

          初始化說明

          void init(PoolSubpage<T> head, int elemSize) {
               doNotDestroy = true;
              // 待分配內(nèi)存
               this.elemSize = elemSize; 
               if (elemSize != 0) {
                      // maxNumElems表示可以被切割成幾份(8192除以待分配內(nèi)存)例如:64=8192/128被切成了64份
                      maxNumElems = numAvail = pageSize / elemSize;
                      nextAvail = 0;
                      // 無符號右移6位,高位補(bǔ)零(相當(dāng)于除以64)例如:64的二進(jìn)制右移6位為1,128的二進(jìn)制右移6位為2
                      bitmapLength = maxNumElems >>> 6;
                      if ((maxNumElems & 63) != 0) { // 相當(dāng)于是否能被64整除
                          bitmapLength ++; // 不能被整除遞增bitmapLength
                      }

                      for (int i = 0; i < bitmapLength; i ++) {
                          bitmap[i] = 0// 等于零表示未被分配
                      }
               }
               addToPool(head);
          }

          過程說明

          @1 先計(jì)算一個(gè)Page被切成了幾份 maxNumElems( pageSize / elemSize)

          @2 計(jì)算bitmap數(shù)組長度bitmapLength(maxNumElems無符號右移6位相當(dāng)于除以64)

          備注: 此處不太好理解為什么要maxNumElems要除以64來計(jì)算bitmap的長度呢?也就是bitmap數(shù)組中的每個(gè)元素可以標(biāo)記64個(gè)被切的內(nèi)存塊。bitmap是long數(shù)組,每個(gè)long類型是64位,他用每個(gè)二進(jìn)制位來標(biāo)記被切內(nèi)存塊的分配情況。

          加入鏈表

          新構(gòu)建的PoolSubpage與tinySubpagePools中的PoolSubpage建成鏈表關(guān)系。

          private void addToPool(PoolSubpage<T> head) {
              assert prev == null && next == null;
              prev = head;
              next = head.next;
              next.prev = this;
              head.next = this;
          }

          小結(jié): 構(gòu)造的PoolSubpage中持有了一個(gè)bitmap[]數(shù)組,數(shù)組長度與待分配的內(nèi)存有關(guān)。待分配內(nèi)存大小為elemSize,數(shù)組長度=PageSize/elemSize,并將bitmap數(shù)組的元素標(biāo)記為未分配。

          注解@6 分配內(nèi)存

          內(nèi)存的分配以兩次分配128B內(nèi)存為例觀察期分配過程。

          @Test
          public void testAllocateSubpage() {
             ByteBufAllocator allocator = new PooledByteBufAllocator();
             allocator.directBuffer(128); // 第一次分配
             allocator.directBuffer(128); // 第二次分配
          }

          第一次分配

          第二次分配

          第一次輪詢第一位已被占用,需要向右移位。

          第二次輪詢第二位未被占用。

          第二次分配過程

          兩次內(nèi)存分配圖示

          第一次分配128B圖示

          此時(shí)64位第一位被標(biāo)記為1,bitmap[0] = 1

          第二次分配128B圖示

          此時(shí)64位第二位也被標(biāo)記為1,bitmap[0] = 3



          瀏覽 77
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本东京热网站 | 国产成人精品影视 | 亚洲狼友视频 | 天天爽天天色天天干 | 中文字幕在线免费视频 |