1. 程式人生 > >[編織消息框架][netty源碼分析]14 PoolChunk 的 PoolSubpage

[編織消息框架][netty源碼分析]14 PoolChunk 的 PoolSubpage

ini 構造 free min oid chunk val 一是 一位

final class PoolSubpage<T> implements PoolSubpageMetric {

    //該page分配的chunk
    final PoolChunk<T> chunk;
    //內存使用記錄
    private final long[] bitmap;
    //該page是否已釋放
    boolean doNotDestroy;
    
    //該page在chunk中的id,通過區段計算偏移
    private final int memoryMapIdx;
    //該page在chunk.memory的偏移量
private final int runOffset; //page大小 private final int pageSize; //page切分後每一段的大小 最少等於16通過 PoolArena#normalizeCapacity方法計算 int elemSize; //page包含的段數量 private int maxNumElems; private int bitmapLength; //下一個可用的位置,用於優化findNextAvail計算 private int nextAvail;
//可用的段數量 private int numAvail; //每個分頁大小 pageSize = 8192(8k) //分頁裏每個單元大小 elemSize最少等於16 通過 PoolArena#normalizeCapacity方法計算 void init(PoolSubpage<T> head, int elemSize) { doNotDestroy = true; this.elemSize = elemSize; if (elemSize != 0) { maxNumElems
= numAvail = pageSize / elemSize; nextAvail = 0; //所有數據除以64分段 >>>6 等於 /(6*6) bitmapLength = maxNumElems >>> 6; //最後邊界加1處理 if ((maxNumElems & 63) != 0) { bitmapLength ++; } //初始化bitmap清零 for (int i = 0; i < bitmapLength; i ++) { bitmap[i] = 0; } } } long allocate() { if (elemSize == 0) { return toHandle(0); } if (numAvail == 0 || !doNotDestroy) { return -1; } final int bitmapIdx = getNextAvail(); //計算出在那一段 int q = bitmapIdx >>> 6; //等於(運算值+1)的倍數,那麽結果就為0。如bits & 63 當bits等於64的倍數時結果為0,否則取min(bits,64) 或 bits/64 余數 //這裏意思是取當前段的所有狀態 int r = bitmapIdx & 63; //設置相應位置狀態 bitmap[q] |= 1L << r; //沒有可以執行回收 if (-- numAvail == 0) { removeFromPool(); } return toHandle(bitmapIdx); } //這裏做了優化,當執行free時會重新計算nextAvail,這時可直接返回 private int getNextAvail() { int nextAvail = this.nextAvail; if (nextAvail >= 0) { this.nextAvail = -1; return nextAvail; } return findNextAvail(); } private int findNextAvail() { final long[] bitmap = this.bitmap; final int bitmapLength = this.bitmapLength; for (int i = 0; i < bitmapLength; i ++) { long bits = bitmap[i]; //取反不等於0說明還有空位可以使用 if (~bits != 0) { return findNextAvail0(i, bits); } } return -1; } private int findNextAvail0(int i, long bits) { final int maxNumElems = this.maxNumElems; //算出在那一段基礎值 final int baseVal = i << 6; //這裏用到 >>> 即每位檢查是否占用,如果沒有占到計算實際id返回 val = baseVal | j for (int j = 0; j < 64; j ++) { // bits & 1 轉換成 bits & b01 該第一位二進制沒有狀態說明該位置沒使用 if ((bits & 1) == 0) { //算出當前段第幾位 int val = baseVal | j; //少於maxNumElems直接返回,否則返回-1 if (val < maxNumElems) { return val; } else { break; } } //>>>= 右移(向低位移動),左邊空出位(高位)以0填充 //>>>1降低一位 bits >>>= 1; } return -1; } private long toHandle(int bitmapIdx) { return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx; } //返回true已在使用,返回false已釋放 boolean free(PoolSubpage<T> head, int bitmapIdx) { if (elemSize == 0) { return true; } int q = bitmapIdx >>> 6; int r = bitmapIdx & 63; bitmap[q] ^= 1L << r; nextAvail = bitmapIdx; if (numAvail ++ == 0) { addToPool(head); return true; } if (numAvail != maxNumElems) { return true; } else { // Subpage not in use (numAvail == maxNumElems) if (prev == next) { // Do not remove if this subpage is the only one left in the pool. //subpage是pool唯一的,不能刪除 return true; } //設置刪除記錄 doNotDestroy = false; removeFromPool(); return false; } } }

通過PoolSubpage構造時打斷點追蹤如何分配pageSize,elemSize

PoolArena normalizeCapacity方法分配 elemSize

static int normalizeCapacity(int reqCapacity) {
        // 大等於512時 雙倍增加
        if ((reqCapacity & -512) != 0) {
            // 這裏為什麽不用 normalizedCapacity<<1 直接加倍,有可能normalizedCapacity剛好是512倍數

            int normalizedCapacity = reqCapacity;
            // 減一是避免雙倍自增
            normalizedCapacity--;
            // 將低四位的二進制都設置為1,一個int是32位
            // 註釋掉代碼是優化過的,邏輯用for簡明示例
            // normalizedCapacity |= normalizedCapacity >>> 1;
            // normalizedCapacity |= normalizedCapacity >>> 2;
            // normalizedCapacity |= normalizedCapacity >>> 4;
            // normalizedCapacity |= normalizedCapacity >>> 8;
            // normalizedCapacity |= normalizedCapacity >>> 16;
            for (int i = 1; i <= 16; i++) {
            normalizedCapacity |= reqCapacity >>> i;
            }
            // 最後要加回1
            normalizedCapacity++;

            // 少於0去掉最高位1即可變正數
            if (normalizedCapacity < 0) {
            normalizedCapacity >>>= 1;
            }

            return normalizedCapacity;
        }
        // 少於512情況
        // 剛好16倍數直接返回
        if ((reqCapacity & 15) == 0) {
            return reqCapacity;
        }
        // &~15 相當於 &-16
        // 如果少於16結果為0,否則大於16取16的倍數
        return (reqCapacity & ~15) + 16;
    }

我們使用簡化的方式來實現PoolSubpage bitmap處理邏輯,方便讀者明白如何用long來做儲存,應用大量的 & | >>> 等運算

public class TestPoolSubpage {
    private static int pageSize = 1024*8;
    private static int maxNumElems = pageSize / 16;
    private static int bitmapLength = maxNumElems >> 6;
    static{
     if ((maxNumElems & 63) != 0) {
             bitmapLength ++;
         } 
    }
    private static List<Boolean>[] bitmap = new ArrayList[bitmapLength];

    public static void main(String[] args) {
        for (int i = 0; i < bitmapLength; i++) {
            bitmap[i] = new ArrayList<>(64);
        }
        for (int i = 0; i < 65; i++) {
            findNextAvail();
        }
    }

    private static void findNextAvail() {
        for (int i = 0; i < bitmapLength; i++) {
            if (bitmap[i].size() == 64) {
                continue;
            }
            for (int j = 0; i < 64; j++) {
            if (bitmap[i].size() <= j || !bitmap[i].get(j)) {
                if (bitmap[i].size() == j) {
                bitmap[i].add(true);
                } else {
                bitmap[i].set(i, true);
                }
                System.out.println("id :" + (i*64 + j) + " section :" + i);
                break;
            }
            }
            break;
        }
        System.out.println();
    }
}

未完侍。。。

[編織消息框架][netty源碼分析]14 PoolChunk 的 PoolSubpage