這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何理解Netty內(nèi)存管理 PoolChunk,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新新互聯(lián),憑借十余年的成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作經(jīng)驗(yàn),本著真心·誠心服務(wù)的企業(yè)理念服務(wù)于成都中小企業(yè)設(shè)計(jì)網(wǎng)站有近千家案例。做網(wǎng)站建設(shè),選成都創(chuàng)新互聯(lián)。
多年之前,從C內(nèi)存的手動(dòng)管理上升到j(luò)ava的自動(dòng)GC,是歷史的巨大進(jìn)步。然而多年之后,netty的內(nèi)存實(shí)現(xiàn)又曲線的回到了手動(dòng)管理模式,正印證了馬克思哲學(xué)觀:社會(huì)總是在螺旋式前進(jìn)的,沒有永遠(yuǎn)的最好。的確,就內(nèi)存管理而言,GC給程序員帶來的價(jià)值是不言而喻的,不僅大大的降低了程序員的負(fù)擔(dān),而且也極大的減少了內(nèi)存管理帶來的Crash困擾,不過也有很多情況,可能手動(dòng)的內(nèi)存管理更為合適。
接下去準(zhǔn)備幾個(gè)篇幅對Netty的內(nèi)存管理進(jìn)行深入分析。
PoolChunk
為了能夠簡單的操作內(nèi)存,必須保證每次分配到的內(nèi)存時(shí)連續(xù)的。Netty中底層的內(nèi)存分配和回收管理主要由PoolChunk實(shí)現(xiàn),其內(nèi)部維護(hù)一棵平衡二叉樹memoryMap,所有子節(jié)點(diǎn)管理的內(nèi)存也屬于其父節(jié)點(diǎn)。
poolChunk默認(rèn)由2048個(gè)page組成,一個(gè)page默認(rèn)大小為8k,圖中節(jié)點(diǎn)的值為在數(shù)組memoryMap的下標(biāo)。
1、如果需要分配大小8k的內(nèi)存,則只需要在第11層,找到第一個(gè)可用節(jié)點(diǎn)即可。
2、如果需要分配大小16k的內(nèi)存,則只需要在第10層,找到第一個(gè)可用節(jié)點(diǎn)即可。
3、如果節(jié)點(diǎn)1024存在一個(gè)已經(jīng)被分配的子節(jié)點(diǎn)2048,則該節(jié)點(diǎn)不能被分配,如需要分配大小16k的內(nèi)存,這個(gè)時(shí)候節(jié)點(diǎn)2048已被分配,節(jié)點(diǎn)2049未被分配,就不能直接分配節(jié)點(diǎn)1024,因?yàn)樵摴?jié)點(diǎn)目前只剩下8k內(nèi)存。
poolChunk內(nèi)部會(huì)保證每次分配內(nèi)存大小為8K*(2n),為了分配一個(gè)大小為chunkSize/(2k)的節(jié)點(diǎn),需要在深度為k的層從左開始匹配節(jié)點(diǎn),那么如何快速的分配到指定內(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ù)組中每個(gè)位置保存的是該節(jié)點(diǎn)所在的層數(shù),有什么作用?對于節(jié)點(diǎn)512,其層數(shù)是9,則:
1、如果memoryMap[512] = 9,則表示其本身到下面所有的子節(jié)點(diǎn)都可以被分配;
2、如果memoryMap[512] = 10, 則表示節(jié)點(diǎn)512下有子節(jié)點(diǎn)已經(jīng)分配過,則該節(jié)點(diǎn)不能直接被分配,而其子節(jié)點(diǎn)中的第10層還存在未分配的節(jié)點(diǎn);
3、如果memoryMap[512] = 12 (即總層數(shù) + 1), 可分配的深度已經(jīng)大于總層數(shù), 則表示該節(jié)點(diǎn)下的所有子節(jié)點(diǎn)都已經(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時(shí),使用allocateRun實(shí)現(xiàn)內(nèi)存分配。
2、否則使用方法allocateSubpage分配內(nèi)存,在allocateSubpage實(shí)現(xiàn)中,會(huì)把一個(gè)page分割成多段,進(jìn)行內(nèi)存分配。
這里先看看allocateRun是如何實(shí)現(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)存,實(shí)際申請的內(nèi)存大小為1024。
2、d = maxOrder - (log2(normCapacity) - pageShifts) 可以確定需要在二叉樹的d層開始節(jié)點(diǎn)匹配。
其中pageShifts默認(rèn)值為13,為何是13?因?yàn)橹挥挟?dāng)申請內(nèi)存大小大于2^13(8192)時(shí)才會(huì)使用方法allocateRun分配內(nèi)存。
3、方法allocateNode實(shí)現(xiàn)在二叉樹中進(jìn)行節(jié)點(diǎn)匹配,具體實(shí)現(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é)點(diǎn)開始遍歷,如果當(dāng)前節(jié)點(diǎn)的val<d,則通過id <<=1匹配下一層;
2、如果val > d,則表示存在子節(jié)點(diǎn)被分配的情況,而且剩余節(jié)點(diǎn)的內(nèi)存大小不夠,此時(shí)需要在兄弟節(jié)點(diǎn)上繼續(xù)查找;
3、分配成功的節(jié)點(diǎn)需要標(biāo)記為不可用,防止被再次分配,在memoryMap對應(yīng)位置更新為12;
4、分配節(jié)點(diǎn)完成后,其父節(jié)點(diǎn)的狀態(tài)也需要更新,并可能引起更上一層父節(jié)點(diǎn)的更新,實(shí)現(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é)點(diǎn)2048被分配出去,更新過程如下:
到目前為止,基于poolChunk的節(jié)點(diǎn)分配已經(jīng)完成。
上述就是小編為大家分享的如何理解Netty內(nèi)存管理 PoolChunk了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
本文名稱:如何理解Netty內(nèi)存管理PoolChunk
網(wǎng)頁URL:http://muchs.cn/article24/jpcsje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、電子商務(wù)、網(wǎng)站收錄、虛擬主機(jī)、軟件開發(fā)、自適應(yīng)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)