Netty原始碼—六、tiny、small記憶體分配
tiny記憶體分配
tiny記憶體分配流程:
如果申請的是tiny型別,會先從tiny快取中嘗試分配,如果快取分配成功則返回
否則從tinySubpagePools中嘗試分配
如果上面沒有分配成功則使用allocateNormal進行分配
從快取中分配
這裡以啟用了快取為例來說明,使用到的快取類是PoolThreadCache,快取是透過佇列實現的,一個佇列中儲存的記憶體大小都是相同的
// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf, int)// 這裡的cache是PoolThreadCacheif (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) { // was able to allocate out of the cache so move on return; }boolean allocateTiny(PoolArena> area, PooledByteBuf> buf, int reqCapacity, int normCapacity) { // 快取維護了一個佇列,這個佇列中儲存的記憶體塊大小都相同 // 找到快取之後,從佇列中取出一個記憶體塊用來初始化buffer return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity); }// 查詢快取陣列中快取的記憶體private MemoryRegionCache> cacheForTiny(PoolArena> area, int normCapacity) { // 計算出申請記憶體位於快取陣列中的位置,即陣列下標 int idx = PoolArena.tinyIdx(normCapacity); if (area.isDirect()) { // 使用直接記憶體的快取 return cache(tinySubPageDirectCaches, idx); } // 使用堆記憶體的快取 return cache(tinySubPageHeapCaches, idx); }static int tinyIdx(int normCapacity) { // 由於tiny快取陣列大小是32,依次對應的記憶體大小是16、32...,512,所以陣列的下標應該是申請記憶體的大小除以16 // normCapacity = normCapacity / 16 return normCapacity >>> 4; }
從快取中分配記憶體的過程
尋找快取tinySubPageDirectCaches
使用快取中的chunk初始化buf
關於tiny快取的資料結構
// tiny快取// 是一個SubPageMemoryRegionCache陣列,預設快取陣列長度:32,依次儲存的記憶體大小是16、32、48...,512io.netty.buffer.PoolThreadCache#tinySubPageDirectCaches// 初始化tiny快取陣列,陣列大小是32// static final int numTinySubpagePools = 512 >>> 4;tinySubPageDirectCaches = createSubPageCaches( tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);// 初始化tiny和small快取陣列private staticMemoryRegionCache [] createSubPageCaches( int cacheSize, int numCaches, SizeClass sizeClass) { if (cacheSize > 0) { @SuppressWarnings("unchecked") MemoryRegionCache [] cache = new MemoryRegionCache[numCaches]; for (int i = 0; i (cacheSize, sizeClass); } return cache; } else { return null; } }
快取使用佇列實現,一個佇列最大元素個數
DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
從tinySubpagePools中分配
上面快取中如果沒有分配到記憶體的話,會向記憶體池tinySubpagePools申請,主要邏輯是:
計算tinySubpagePools陣列的index,右移4,除以16(該陣列長度是512>>>4)
取出index出的subpage,也就是這個index處head
從head.next查詢合適的記憶體
找到可用記憶體後使用io.netty.buffer.PoolChunk#initBufWithSubpage(io.netty.buffer.PooledByteBuf
前面已經介紹過tinySubpagePools,是一個陣列,陣列大小是32,每個元素是一個PoolSubpage,PoolSubpage本身是一個連結串列,所以要在這個裡面查詢可用記憶體,先要計算出陣列下表,然後找到該位置的PoolSubpage,取出這個連結串列的頭,然後分配記憶體。關鍵程式碼如下
// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf, int)private void allocate(PoolThreadCache cache, PooledByteBuf buf, final int reqCapacity) { final int normCapacity = normalizeCapacity(reqCapacity); if (isTinyOrSmall(normCapacity)) { // capacity [] table; boolean tiny = isTiny(normCapacity); if (tiny) { // head = table[tableIdx]; /** * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and * {@link PoolChunk#free(long)} may modify the doubly linked list as well. */ synchronized (head) { final PoolSubpage s = head.next; // 空連結串列的話,head指向自己 if (s != head) { assert s.doNotDestroy && s.elemSize == normCapacity; long handle = s.allocate(); assert handle >= 0; s.chunk.initBufWithSubpage(buf, handle, reqCapacity); incTinySmallAllocation(tiny); return; } } // 省略中間程式碼... }
為什麼上面沒有沒有判斷head.next是否有可用的記憶體呢?
tinySubpagePools裡面儲存的是已經被分配過部分記憶體的PoolSubpage,PoolSubpage本身是一個連結串列,如果連結串列中除了head外有其他PoolSubpage,那麼這個subpage一定有可以用的記憶體塊,因為在PoolSubpage.allocate的時候,如果發現沒有可用的記憶體塊了,會將subpage從連結串列中移除。
按照normal的方式分配
如果上面兩種方法都沒有分配到記憶體,則呼叫allocateNormal方法來分配記憶體,allocateNormal獲取記憶體的方法前面已經說過了,這裡不再贅述。
small記憶體分配
small的分配過程和tiny記憶體的分配過程幾乎一致
small記憶體分配流程:
如果申請的是tiny型別,會先從small快取中嘗試分配,如果快取分配成功則返回
否則從smallSubpagePools中嘗試分配
如果上面沒有分配成功則使用allocateNormal進行分配
small記憶體的獲取過程和tiny的方式類似,可以對照學習,這裡不再詳述。
原文出處:https://www.cnblogs.com/sunshine-2015/p/9380751.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/810/viewspace-2805589/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Netty 中的記憶體分配淺析Netty記憶體
- Netty原始碼解析 -- 記憶體池與PoolArenaNetty原始碼記憶體
- Netty原始碼解析 -- 記憶體對齊類SizeClassesNetty原始碼記憶體
- Netty 中的記憶體分配淺析-資料容器Netty記憶體
- mimalloc記憶體分配程式碼分析記憶體
- 垃圾收集器與記憶體分配策略_記憶體分配策略記憶體
- 動態記憶體分配記憶體
- 記憶體的分配與釋放,記憶體洩漏記憶體
- java-方法記憶體分配Java記憶體
- go記憶體分配器Go記憶體
- java基礎-記憶體分配Java記憶體
- hadoop 記憶體分配規則Hadoop記憶體
- C語言-記憶體分配C語言記憶體
- 記憶體分配策略學習記憶體
- 深度理解glibc記憶體分配記憶體
- linux記憶體管理(一)實體記憶體的組織和記憶體分配Linux記憶體
- Swoole 原始碼分析——記憶體模組之記憶體池原始碼記憶體
- 【Java】 記憶體分配全面淺析Java記憶體
- JVM GC 與 記憶體分配策略JVMGC記憶體
- C++動態記憶體分配C++記憶體
- 記憶體分配問題處理記憶體
- C語言的記憶體分配C語言記憶體
- C中的記憶體分配模型記憶體模型
- [20210126]探究oracle記憶體分配.txtOracle記憶體
- Python如何管理記憶體?記憶體分配機制是什麼?Python記憶體
- jvm:記憶體模型、記憶體分配及GC垃圾回收機制JVM記憶體模型GC
- Netty原始碼分析--Channel註冊(中)(六)Netty原始碼
- Memcached記憶體管理原始碼分析記憶體原始碼
- 深入理解Java虛擬機器筆記之六記憶體分配與回收策略Java虛擬機筆記記憶體
- Netty原始碼學習8——從ThreadLocal到FastThreadLocal(如何讓FastThreadLocal記憶體洩漏doge)Netty原始碼threadAST記憶體
- MySQL記憶體管理,記憶體分配器和作業系統MySql記憶體作業系統
- 圖解Go語言記憶體分配圖解Go記憶體
- Android O 8.0 以上 bitmap記憶體分配Android記憶體
- v8記憶體分配淺談記憶體
- curl 中減少記憶體分配操作記憶體
- JVM 之 記憶體分配與回收策略JVM記憶體
- 深入理解golang:記憶體分配原理Golang記憶體
- Java記憶體區域與分配策略Java記憶體