深入淺出Netty記憶體管理:PoolSubpage

佔小狼發表於2016-09-23

本系列:


上一節中分析瞭如何在poolChunk中分配一塊大於pageSize的記憶體,但在實際應用中,存在很多分配小記憶體的情況,如果也佔用一個page,明顯很浪費。針對這種情況,Netty提供了PoolSubpage把poolChunk的一個page節點8k記憶體劃分成更小的記憶體段,通過對每個記憶體段的標記與清理標記進行記憶體的分配與釋放。

01
PoolSubpage

假設目前需要申請大小為4096的記憶體:

因為 4096 < pageSize(8192),所以採用 allocateSubpage 進行記憶體分配,具體實現如下:

1、Arena負責管理PoolChunk和PoolSubpage;
2、allocateNode負責在二叉樹中找到匹配的節點,和poolChunk不同的是,只匹配葉子節點;
3、poolChunk中維護了一個大小為2048的poolSubpage陣列,分別對應二叉樹中2048個葉子節點,假設本次分配到節點2048,則取出poolSubpage陣列第一個元素subpage;
4、如果subpage為空,則進行初始化,並加入到poolSubpage陣列;

subpage初始化實現如下:

1、預設初始化bitmap長度為8,這裡解釋一下為什麼只需要8個元素:其中分配記憶體大小都是處理過的,最小為16,說明一個page可以分成8192/16 = 512個記憶體段,一個long有64位,可以描述64個記憶體段,這樣只需要512/64 = 8個long就可以描述全部記憶體段了。
2、init根據當前需要分配的記憶體大小,確定需要多少個bitmap元素,實現如下:

下面通過分佈申請4096和32大小的記憶體,說明如何確定bitmapLength的值:

  1. 比如,當前申請大小4096的記憶體,maxNumElems 和 numAvail 為2,說明一個page被拆分成2個記憶體段,2 >>> 6 = 0,且2 & 63 != 0,所以bitmapLength為1,說明只需要一個long就可以描述2個記憶體段狀態。
  2. 如果當前申請大小32的記憶體,maxNumElems 和 numAvail 為 256,說明一個page被拆分成256個記憶體段, 256>>> 6 = 4,說明需要4個long描述256個記憶體段狀態。

下面看看subpage是如何進行記憶體分配的:

1、方法getNextAvail負責找到當前page中可分配記憶體段的bitmapIdx;
2、q = bitmapIdx >>> 6,確定bitmap陣列下標為q的long數,用來描述 bitmapIdx 記憶體段的狀態;
3、bitmapIdx & 63將超出64的那一部分二進位制數抹掉,得到一個小於64的數r;
4、bitmap[q] |= 1L << r將對應位置q設定為1;

如果以上描述不直觀的話,下面換一種方式解釋,假設需要分配大小為128的記憶體,這時page會拆分成64個記憶體段,需要1個long型別的數字描述這64個記憶體段的狀態,所以bitmap陣列只會用到第一個元素。

02
狀態轉換

getNextAvail如何實現找到下一個可分配的記憶體段?

1、如果nextAvail大於等於0,說明nextAvail指向了下一個可分配的記憶體段,直接返回nextAvail值;
2、每次分配完成,nextAvail被置為-1,這時只能通過方法findNextAvail重新計算出下一個可分配的記憶體段位置。

1、~bits != 0說明這個long所描述的64個記憶體段還有未分配的;
2、(bits & 1) == 0 用來判斷該位置是否未分配,否則bits又移一位,從左到右遍歷值為0的位置;

至此,subpage記憶體段已經分配完成。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

深入淺出Netty記憶體管理:PoolSubpage

相關文章