深入淺出Netty記憶體管理:PoolChunk
多年之前,從C記憶體的手動管理上升到java的自動GC,是歷史的巨大進步。然而多年之後,netty的記憶體實現又曲線的回到了手動管理模式,正印證了馬克思哲學觀:社會總是在螺旋式前進的,沒有永遠的最好。的確,就記憶體管理而言,GC給程式設計師帶來的價值是不言而喻的,不僅大大的降低了程式設計師的負擔,而且也極大的減少了記憶體管理帶來的Crash困擾,不過也有很多情況,可能手動的記憶體管理更為合適。
接下去準備幾個篇幅對Netty的記憶體管理進行深入分析。
PoolChunk
為了能夠簡單的操作記憶體,必須保證每次分配到的記憶體時連續的。Netty中底層的記憶體分配和回收管理主要由PoolChunk實現,其內部維護一棵平衡二叉樹memoryMap,所有子節點管理的記憶體也屬於其父節點。
memoryMap
poolChunk預設由2048個page組成,一個page預設大小為8k,圖中節點的值為在陣列memoryMap的下標。
1、如果需要分配大小8k的記憶體,則只需要在第11層,找到第一個可用節點即可。
2、如果需要分配大小16k的記憶體,則只需要在第10層,找到第一個可用節點即可。
3、如果節點1024存在一個已經被分配的子節點2048,則該節點不能被分配,如需要分配大小16k的記憶體,這個時候節點2048已被分配,節點2049未被分配,就不能直接分配節點1024,因為該節點目前只剩下8k記憶體。
poolChunk內部會保證每次分配記憶體大小為8K*(2^n),為了分配一個大小為chunkSize/(2^k)的節點,需要在深度為k的層從左開始匹配節點,那麼如何快速的分配到指定記憶體?
memoryMap初始化:
123456789101112 | memoryMap=newbyte[maxSubpageAllocs<<1];depthMap=newbyte[memoryMap.length];intmemoryMapIndex=1;for(intd=0;d<=maxOrder;++d){// move down the tree one level at a timeintdepth=1<<d;for(intp=0;p<depth;++p){// in each level traverse left to right and set value to the depth of subtreememoryMap[memoryMapIndex]=(byte)d;depthMap[memoryMapIndex]=(byte)d;memoryMapIndex++;}} |
memoryMap陣列中每個位置儲存的是該節點所在的層數,有什麼作用?對於節點512,其層數是9,則:
1、如果memoryMap[512] = 9,則表示其本身到下面所有的子節點都可以被分配;
2、如果memoryMap[512] = 10, 則表示節點512下有子節點已經分配過,則該節點不能直接被分配,而其子節點中的第10層還存在未分配的節點;
3、如果memoryMap[512] = 12 (即總層數 + 1), 可分配的深度已經大於總層數, 則表示該節點下的所有子節點都已經被分配。
下面看看如何向PoolChunk申請一段記憶體:
1234567 | longallocate(intnormCapacity){if((normCapacity&subpageOverflowMask)!=0){// >= pageSizereturnallocateRun(normCapacity);}else{returnallocateSubpage(normCapacity);}} |
1、當需要分配的記憶體大於pageSize時,使用allocateRun實現記憶體分配。
2、否則使用方法allocateSubpage分配記憶體,在allocateSubpage實現中,會把一個page分割成多段,進行記憶體分配。
這裡先看看allocateRun是如何實現的:
123456789 | privatelongallocateRun(intnormCapacity){intd=maxOrder-(log2(normCapacity)-pageShifts);intid=allocateNode(d);if(id<0){returnid;}freeBytes-=runLength(id);returnid;} |
1、normCapacity是處理過的值,如申請大小為1000的記憶體,實際申請的記憶體大小為1024。
2、d = maxOrder – (log2(normCapacity) – pageShifts) 可以確定需要在二叉樹的d層開始節點匹配。
其中pageShifts預設值為13,為何是13?因為只有當申請記憶體大小大於2^13(8192)時才會使用方法allocateRun分配記憶體。
3、方法allocateNode實現在二叉樹中進行節點匹配,具體實現如下:
1234567891011121314151617181920212223 | privateintallocateNode(intd){intid=1;intinitial=-(1<<d);//value(id)=memoryMap[id] byteval=value(id);if(val>d){// unusablereturn-1;}while(val<d||(id&initial)==0){// id & initial == 1 << d for all ids at depth d, for < d it is 0id<<=1;val=value(id);if(val>d){id^=1;val=value(id);}}bytevalue=value(id);assertvalue==d&&(id&initial)==1<<d:String.format("val = %d, id & initial = %d, d = %d",value,id&initial,d);setValue(id,unusable);// mark as unusableupdateParentsAlloc(id);returnid;} |
- 從根節點開始遍歷,如果當前節點的val<d,則通過id <<=1匹配下一層;
2、如果val > d,則表示存在子節點被分配的情況,而且剩餘節點的記憶體大小不夠,此時需要在兄弟節點上繼續查詢;
3、分配成功的節點需要標記為不可用,防止被再次分配,在memoryMap對應位置更新為12;
4、分配節點完成後,其父節點的狀態也需要更新,並可能引起更上一層父節點的更新,實現如下:
12345678910 | privatevoidupdateParentsAlloc(intid){while(id>1){intparentId=id>>>1;byteval1=value(id);byteval2=value(id^1);byteval=val1<val2?val1:val2;setValue(parentId,val);id=parentId;}} |
比如節點2048被分配出去,更新過程如下:
memoryMap節點更新
到目前為止,基於poolChunk的節點分配已經完成。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
相關推薦
深入淺出Netty記憶體管理:PoolChunk
多年之前,從C記憶體的手動管理上升到java的自動GC,是歷史的巨大進步。然而多年之後,netty的記憶體實現又曲線的回到了手動管理模式,正印證了馬克思哲學觀:社會總是在螺旋式前進的,沒有永遠的最好。的確,就記憶體管理而言,GC給程式設計師帶來的價值是不言而喻的,
深入淺出Netty記憶體管理:PoolSubpage
本系列: 上一節中分析瞭如何在poolChunk中分配一塊大於pageSize的記憶體,但在實際應用中,存在很多分配小記憶體的情況,如果也佔用一個page,明顯很浪費。針對這種情況,Netty提供了PoolSubpage把poolChunk的一個page節點8k
深入淺出Netty記憶體管理:PoolArena
前面分別分析了PoolChunk、PoolSubpage和PoolChunkList,本文主要分析PoolArena。 PoolArena 應用層的記憶體分配主要通過如下實現,但最終還是委託給PoolArena實現。 PooledByteBufAllocat
深入淺出Netty記憶體管理:PoolChunkList
前面兩篇分別分析了PoolChunk和PoolSubpage的實現,本文主要分析管理PoolChunk生命週期的PoolChunkList。 PoolChunkList PoolChunkList負責管理多個chunk的生命週期,在此基礎上對記憶體分配進行進一步的優化。
資料庫記憶體管理:tcmalloc與jemalloc
[[email protected] ~]# tar zxvf libunwind-1.2.1.tar.gz [[email protected] ~]# cd libunwind-1.2
Sqlserver記憶體管理:限制最大佔用記憶體
一、Sqlserver對系統記憶體的管理原則是:按需分配,且貪婪(用完不還)。它不會自動釋放記憶體,因此執行結果集大的sql語句時,資料取出後,會一直佔用記憶體,直到佔滿機器記憶體(並不會撐滿,還是有個最大限制,比機器記憶體稍小),在重啟服務前,sqlserver不會釋放該記
Spark靜態記憶體管理:StaticMemoryManager
例如Executor的可用Heap大小是10G,實際上Spark只能使用90%,也就是9G的大小,是由spark.storage.safetyFraction來控制。 Spark1.6.X以前JVM到底可以快取多少資料? (1)單個Executor的Cache資料量計算公式: Heap Size * spar
iOS效能優化之記憶體管理:Analyze、Leaks、Allocations的使用和案例程式碼
一. 一些相關概念 1.記憶體空間的劃分: 我們知道,一個程序佔用的記憶體空間,包含5種不同的資料區:(1)BSS段:通常是存放未初始化的全域性變數;(2)資料段:通常是存放已初始化的全域性變數。(3)程式碼段:通常是存放程式執行程式碼。(4)堆:通常是用於存放程序執行中被
Linux記憶體管理:HighMemory
HighMemory介紹 Linux一般把整個4GB可以map的記憶體中的1GB用於低端記憶體。從0xC0000000開始的話(CONFIG_PAGE_OFFSET配置),低端記憶體的地址範圍就是0xC0000000到high_memory地址。 high_
iOS記憶體管理:記憶體洩露除錯的常用技巧
在往下看之前請下載例項MemoryProblems,我們將以這個工程展開如何檢查和解決記憶體問題。 懸掛指標問題 懸掛指標(Dangling Pointer)就是當指標指向的物件已經釋放或回收後,但沒有對指標做任何修改(一般來說,將它指向空指標),而是仍然指向原來已經回收的地址。如
JVM記憶體管理:記憶體區域和記憶體洩漏
VM執行時資料區域 JVM執行Java程式的過程中,會使用到各種資料區域,這些區域有各自的用途、建立和銷燬時間。根據《Java虛擬機器規範(第二版)》(下文稱VM Spec)的規定,JVM包括下列幾個執行時資料區域: 1.程式計數器(Program Counter Register): 每一個Java執
Linux記憶體管理:CMA
某些驅動需要用到一大塊連續的實體記憶體,但使用kmalloc等很分配很大的連續記憶體。 所以這裡有一種三星實現叫CMA的方式,來連續的大記憶體分配。 Why is it needed? Issue 1: Camera, Video Codec等Multimedia Device需要連續的數MB大小的Me
C語言動態記憶體管理:malloc、realloc、calloc以及free函式
我們已經掌握的記憶體開闢方式有: int val = 20;//在棧空間上開闢四個位元組 char arr[10] = {0};//在棧空間上開闢10個位元組的連續空間 但是這種開闢空間的方式有兩個特點: 1. 空間開闢的大小是固定的。
作業系統的學習(2)——實體記憶體管理:連續記憶體分配
記憶體的最小訪問單位是位元組(8it),一般計算機系統是32位匯流排,一次讀寫可以讀或者寫32位也就是4位元組。 CPU裡會看到快取記憶體,快取記憶體就是在進行讀寫指令或者指令執行的過程中,訪問資料都需要從記憶體中讀資料,如果這時候有大量資料需要讀寫或者重複利
Netty記憶體池之PoolChunk原理詳解
開發十年,就只剩下這套架構體系了! >>>
7.netty記憶體管理-ByteBuf
ByteBuf ByteBuf是什麼 ByteBuf重要API read、write、set、skipBytes mark和reset duplicate、slice、copy retain、release ByteBuf擴容 ByteBuf種類 ByteBufAllocate UnPooledB
談談Netty記憶體管理
## 前言 正是Netty的易用性和高效能成就了Netty,讓其能夠如此流行。 而作為一款通訊框架,首當其衝的便是對IO效能的高要求。 不少讀者都知道Netty底層通過使用Direct Memory,減少了核心態與使用者態之間的記憶體拷貝,加快了IO速率。但是頻繁的向系統申請Direct Memory
深入淺出Netty內存管理 PoolChunk
mov format for 通過 程序員 overflow 平衡 根節點 upd 多年之前,從C內存的手動管理上升到java的自動GC,是歷史的巨大進步。然而多年之後,netty的內存實現又曲線的回到了手動管理模式,正印證了馬克思哲學觀:社會總是在螺旋式前進的,沒有永遠的
第四節:FreeRTOS 記憶體管理
目錄 記憶體管理的介紹 記憶體碎片 Heap_1-5記憶體分配的區別 Heap_1:適用於一旦建立好記憶體,就不刪除的任務。 (本質是分配的大陣列做記憶體堆.) Heap_2:適用於重複分配和刪除具有相同堆疊空間任務。(本質是分配的大
QEMU深入淺出: guest實體記憶體管理
原 文:http://blog.vmsplice.net/2016/01/qemu-internals-how-guest-physical-ram.html 作 者:Stefan Hajnoczi 領 域:Open source and virtuali