PoolSubpage源碼分析-這個方法獲取一個long表示的64個比特位置是否還有空餘空間
private int findNextAvail() {
// 沒有明確的可用位置時則挨個查找
final long[] bitmap = this.bitmap;
final int bitmapLength = this.bitmapLength;
for (int i = 0; i < bitmapLength; i ++) {
long bits = bitmap[i];
//~bits 把字節0變1 1變0,如果一個long類型64個比特全部爲1,那麼反過來得0,說明沒有位置了。
// 說明這個位置段中還有可以分配的element
if (~bits != 0) {
return findNextAvail0(i, bits);
}
}
return -1;
}
PoolSubpage源碼分析-這個方法查詢出一個long類型64個比特當中可用的比特位,在加上一個基數,就計算出了對應的內存快的位置。比如當前內存按16字節分塊,那麼8192/16=512,一共內存分成512塊, 在bits當中找到第一個比特爲0的位置,比如70,假設基礎爲64,那麼64+70=134就是對應空閒的內存快。
private int findNextAvail0(int i, long bits) {
final int maxNumElems = this.maxNumElems;
//重點:首先右移位6爲,相當於乘以8, 因爲i的範圍從0-7
//i=0的long的64個比特表示0-63個位置, i=1的long表示64-127的位置。
//所以baseVal相當於這個基數,如果i=0,那麼baseVal=0,如果i=1,baseVal=64, i=2,baseVal=128
final int baseVal = i << 6;
//循環64次,依次拿出long的每個比特進行判斷
for (int j = 0; j < 64; j ++) {
//&倆邊爲1結果爲1否則爲0
//bits & 1 相當於判斷bits的最後一個比特位,
// 如果該位置的值爲0,表示還未分配
if ((bits & 1) == 0) {
// | 倆邊爲0結果爲0 否則爲1
//j從0-63,baseVal | j 相當於 baseVal+j, j就是long裏面可用比特的位置,但是需要加上基數。
int val = baseVal | j;
//這裏要判斷一下,因爲如果當前的ElementSize不是16,假設爲32,那麼maxNumElems=256
//bitmap長度爲8,64x8=512,表示512個位置,但其實最大位置也就256,所以需要判斷一下,只有ElementSize=16時這個判斷纔沒有意義,因爲16會被分成512份,正好。
if (val < maxNumElems) {
return val;
} else {
break;
}
}
//否則無符號右移1爲,相當於把原來bits的第二個比特移到第一個位置。
bits >>>= 1;
}
return -1;
}
PoolSubpage源碼分析-分配一個可用的內存分片。
long allocate() {
//這裏不知道什麼意思
if (elemSize == 0) {
return toHandle(0);
}
//如果可用的內存分片爲0,或者doNotDestroy=false
//說明無內存可用,返回-1
if (numAvail == 0 || !doNotDestroy) {
return -1;
}
//找到一個可用的內存分片的index,假設65
final int bitmapIdx = getNextAvail();
//右移6爲等於除以64, q=1,說明要放在bitmap[1]這個位置
//用bitmap[1]這個long的比特表示120這個位置的狀態。
int q = bitmapIdx >>> 6;
//把高位全部去掉,只留下一個0-63的數(正好64種可能)
//r表示0-63當中的某一個位置 r=1,
//0011010101...(56比特)+(8比特)000000r1, r=65所在的比特位置
//0011010101...(56比特)+(8比特)0000000r, r=64所在的比特位置
int r = bitmapIdx & 63;
//把r移到最右面,必須等於0,所以爲被分配
assert (bitmap[q] >>> r & 1) == 0;
//1L左移R位,相當於複製 在|拼接原始值
bitmap[q] |= 1L << r;
//如果可用內存分片爲0,則從隊列中移除
if (-- numAvail == 0) {
removeFromPool();
}
//返回一個long類型的handle,裏面包含了內存的index,還有當前subpage所屬的16M內存當中的index
return toHandle(bitmapIdx);
}
PoolSubpage源碼分析,這個方法把3個數字按比特排列方式拼接爲1個數字,等使用時在把這三個數字還原。
private long toHandle(int bitmapIdx) {
//把3個數字拼接爲一個,一個long類型64個比特,拼接在一起個佔個的位置,等需要時在進行拆分得到原先的3個數字。
return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
}
PoolSubpage源碼分析添加到隊列和從隊列中移除,當前對象會加入到arena當中管理。
//把隊列插入到頭的後面, 比如原來的順序爲 head - x - y
//當前對象爲this,那麼拼接後的順序爲 head - this -x -y
private void addToPool(PoolSubpage<T> head) {
assert prev == null && next == null;
//當前對象的前一個指head
prev = head;
//當前對象的後一個指head的下一個
next = head.next;
next.prev = this;
head.next = this;
}
//把當前對象從鏈表中移除
//head - x - this - y 移除後 head - x -y
private void removeFromPool() {
assert prev != null && next != null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
}
//構造方法
PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) {
//所屬chunk
this.chunk = chunk;
//所屬chunk整個內存分塊的下標值,默認chunk爲16M,每頁大小8192,分爲2048個塊,那麼memoryMapIdx表代表了哪一塊
this.memoryMapIdx = memoryMapIdx;
//整體內存的偏移量,比如整體內存爲16M的byte素組,那runOffset就是數組下標的起點
this.runOffset = runOffset;
//每頁大小,默認8192
this.pageSize = pageSize;
//long類型素組, pageSize / 16 / 64
//因爲最小單元把當前8192字節拆分爲16個字節一個單元,8192/16=512 個單元
//一個long的64個比特位表示64個單元,那麼需要512/64=8個long類型的數字表示
bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64
init(head, elemSize);
}
void init(PoolSubpage<T> head, int elemSize) {
//設置不可回收
doNotDestroy = true;
//單元大小,16 或 32 或 64 。。。。。
this.elemSize = elemSize;
//假設16比特位單元大小
if (elemSize != 0) {
//8192/16=512
//那麼maxNumElems 和 numAvail = 512
maxNumElems = numAvail = pageSize / elemSize;
//可用位置從0開始
nextAvail = 0;
//512 除以64 = 8,表示bitmap數組需要使用幾個元素,當單元大小爲16時,需要8個元素
//這也是最多的一種情況,如果單元大小爲32時,maxNumElems=256,那麼256/64=4,那麼需要4個元素就能滿足情況了
bitmapLength = maxNumElems >>> 6;
if ((maxNumElems & 63) != 0) {
bitmapLength ++;
}
//把元素都設置爲0
for (int i = 0; i < bitmapLength; i ++) {
bitmap[i] = 0;
}
}
//加入到arena的隊列中管理
addToPool(head);
}
boolean free(PoolSubpage<T> head, int bitmapIdx) {
if (elemSize == 0) {
return true;
}
//假設bitmapIdx=65
//除以64計算出數組下標 q=1
int q = bitmapIdx >>> 6;
//計算出所在0-63當中的位置,r=1
int r = bitmapIdx & 63;
//把值移到最右面 必須不能爲0
assert (bitmap[q] >>> r & 1) != 0;
//如果相對應位值相同,則結果爲0,否則爲1
//左移1爲,得到 xxxx.....1x(64bit)
//把xxxx.....1x 變爲xxxx.....0x,其實就是把r位置的比特設置爲0
bitmap[q] ^= 1L << r;
//設置下一個可用位置爲當前的bitmapIdx
setNextAvail(bitmapIdx);
//先判斷在++,如果numAvail ==0 說明沒在池中,需要加入池,然後++
if (numAvail ++ == 0) {
addToPool(head);
return true;
}
//如果可用單元沒達到最大值,說明還有空餘空間
if (numAvail != maxNumElems) {
return true;
} else {
//如果Subpage空閒
// Subpage not in use (numAvail == maxNumElems)
//判斷一下當前對象是不是arena數組中head的下一個元素,如果是則保留當前對象,否則將其從pool中移除。
//目的:這樣儘可能的保證arena分配小內存時能直接從pool中取,而不用再到chunk中去獲取。
if (prev == next) {
// Do not remove if this subpage is the only one left in the pool.
return true;
}
// Remove this subpage from the pool if there are other subpages left in the pool.
//把當前對象從隊列移除,返回false,調用者會根據返回值做處理,會把他回收掉,同時把父類對應的8192空間釋放爲可用。
doNotDestroy = false;
removeFromPool();
return false;
}
}