真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何理解Netty內(nèi)存管理PoolChunk

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)如何理解Netty內(nèi)存管理 PoolChunk,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新新互聯(lián),憑借十余年的成都網(wǎng)站設(shè)計、網(wǎng)站制作經(jīng)驗,本著真心·誠心服務(wù)的企業(yè)理念服務(wù)于成都中小企業(yè)設(shè)計網(wǎng)站有近千家案例。做網(wǎng)站建設(shè),選成都創(chuàng)新互聯(lián)。

多年之前,從C內(nèi)存的手動管理上升到j(luò)ava的自動GC,是歷史的巨大進(jìn)步。然而多年之后,netty的內(nèi)存實現(xiàn)又曲線的回到了手動管理模式,正印證了馬克思哲學(xué)觀:社會總是在螺旋式前進(jìn)的,沒有永遠(yuǎn)的最好。的確,就內(nèi)存管理而言,GC給程序員帶來的價值是不言而喻的,不僅大大的降低了程序員的負(fù)擔(dān),而且也極大的減少了內(nèi)存管理帶來的Crash困擾,不過也有很多情況,可能手動的內(nèi)存管理更為合適。
接下去準(zhǔn)備幾個篇幅對Netty的內(nèi)存管理進(jìn)行深入分析。
PoolChunk
為了能夠簡單的操作內(nèi)存,必須保證每次分配到的內(nèi)存時連續(xù)的。Netty中底層的內(nèi)存分配和回收管理主要由PoolChunk實現(xiàn),其內(nèi)部維護(hù)一棵平衡二叉樹memoryMap,所有子節(jié)點管理的內(nèi)存也屬于其父節(jié)點。

如何理解Netty內(nèi)存管理 PoolChunk
poolChunk默認(rèn)由2048個page組成,一個page默認(rèn)大小為8k,圖中節(jié)點的值為在數(shù)組memoryMap的下標(biāo)。
1、如果需要分配大小8k的內(nèi)存,則只需要在第11層,找到第一個可用節(jié)點即可。
2、如果需要分配大小16k的內(nèi)存,則只需要在第10層,找到第一個可用節(jié)點即可。
3、如果節(jié)點1024存在一個已經(jīng)被分配的子節(jié)點2048,則該節(jié)點不能被分配,如需要分配大小16k的內(nèi)存,這個時候節(jié)點2048已被分配,節(jié)點2049未被分配,就不能直接分配節(jié)點1024,因為該節(jié)點目前只剩下8k內(nèi)存。
poolChunk內(nèi)部會保證每次分配內(nèi)存大小為8K*(2n),為了分配一個大小為chunkSize/(2k)的節(jié)點,需要在深度為k的層從左開始匹配節(jié)點,那么如何快速的分配到指定內(nèi)存?
memoryMap初始化:

memoryMap = new byte[maxSubpageAllocs << 1];
depthMap = new byte[memoryMap.length];
int memoryMapIndex = 1;
for (int d = 0; d <= maxOrder; ++ d) { // move down the tree one level at a time
    int depth = 1 << d;
    for (int p = 0; p < depth; ++ p) {
        // in each level traverse left to right and set value to the depth of subtree
        memoryMap[memoryMapIndex] = (byte) d;
        depthMap[memoryMapIndex] = (byte) d;
        memoryMapIndex ++;
    }
}

memoryMap數(shù)組中每個位置保存的是該節(jié)點所在的層數(shù),有什么作用?對于節(jié)點512,其層數(shù)是9,則:
1、如果memoryMap[512] = 9,則表示其本身到下面所有的子節(jié)點都可以被分配;
2、如果memoryMap[512] = 10, 則表示節(jié)點512下有子節(jié)點已經(jīng)分配過,則該節(jié)點不能直接被分配,而其子節(jié)點中的第10層還存在未分配的節(jié)點;
3、如果memoryMap[512] = 12 (即總層數(shù) + 1), 可分配的深度已經(jīng)大于總層數(shù),  則表示該節(jié)點下的所有子節(jié)點都已經(jīng)被分配。
下面看看如何向PoolChunk申請一段內(nèi)存:

long allocate(int normCapacity) {
    if ((normCapacity & subpageOverflowMask) != 0) { // >= pageSize
        return allocateRun(normCapacity);
    } else {
        return allocateSubpage(normCapacity);
    }
}

1、當(dāng)需要分配的內(nèi)存大于pageSize時,使用allocateRun實現(xiàn)內(nèi)存分配。
2、否則使用方法allocateSubpage分配內(nèi)存,在allocateSubpage實現(xiàn)中,會把一個page分割成多段,進(jìn)行內(nèi)存分配。

這里先看看allocateRun是如何實現(xiàn)的:

private long allocateRun(int normCapacity) {
    int d = maxOrder - (log2(normCapacity) - pageShifts);
    int id = allocateNode(d);
    if (id < 0) {
        return id;
    }
    freeBytes -= runLength(id);
    return id;
}

1、normCapacity是處理過的值,如申請大小為1000的內(nèi)存,實際申請的內(nèi)存大小為1024。
2、d = maxOrder - (log2(normCapacity) - pageShifts) 可以確定需要在二叉樹的d層開始節(jié)點匹配。
其中pageShifts默認(rèn)值為13,為何是13?因為只有當(dāng)申請內(nèi)存大小大于2^13(8192)時才會使用方法allocateRun分配內(nèi)存。
3、方法allocateNode實現(xiàn)在二叉樹中進(jìn)行節(jié)點匹配,具體實現(xiàn)如下:

private int allocateNode(int d) {
    int id = 1;
    int initial = - (1 << d); 
    //value(id)=memoryMap[id] 
    byte val = value(id); 
    if (val > d) { // unusable
        return -1;
    }
    while (val < d || (id & initial) == 0) { // id & initial == 1 << d for all ids at depth d, for < d it is 0
        id <<= 1;
        val = value(id);
        if (val > d) {
            id ^= 1;
            val = value(id);
        }
    }
    byte value = value(id);
    assert value == d && (id & initial) == 1 << d : String.format("val = %d, id & initial = %d, d = %d",
            value, id & initial, d);
    setValue(id, unusable); // mark as unusable
    updateParentsAlloc(id);
    return id;
}

1、從根節(jié)點開始遍歷,如果當(dāng)前節(jié)點的val2、如果val > d,則表示存在子節(jié)點被分配的情況,而且剩余節(jié)點的內(nèi)存大小不夠,此時需要在兄弟節(jié)點上繼續(xù)查找;
3、分配成功的節(jié)點需要標(biāo)記為不可用,防止被再次分配,在memoryMap對應(yīng)位置更新為12;
4、分配節(jié)點完成后,其父節(jié)點的狀態(tài)也需要更新,并可能引起更上一層父節(jié)點的更新,實現(xiàn)如下:

private void updateParentsAlloc(int id) {
    while (id > 1) {
        int parentId = id >>> 1;
        byte val1 = value(id);
        byte val2 = value(id ^ 1);
        byte val = val1 < val2 ? val1 : val2;
        setValue(parentId, val);
        id = parentId;
    }
}

比如節(jié)點2048被分配出去,更新過程如下:
如何理解Netty內(nèi)存管理 PoolChunk
到目前為止,基于poolChunk的節(jié)點分配已經(jīng)完成。

上述就是小編為大家分享的如何理解Netty內(nèi)存管理 PoolChunk了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


當(dāng)前題目:如何理解Netty內(nèi)存管理PoolChunk
轉(zhuǎn)載注明:http://weahome.cn/article/jpcsje.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部