Netty源碼解析 -- 內存池與PoolArena

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們知道,Netty使用直接內存實現Netty零拷貝以提升性能,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但直接內存的創建和釋放可能需要涉及系統調用,是比較昂貴的操作,如果每個請求都創建和釋放一個直接內存,那性能肯定是不能滿足要求的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這時就需要使用內存池。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即從系統中申請一大塊內存,再在上面分配每個請求所需的內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Netty中的內存池主要涉及PoolArena,PoolChunk與PoolSubpage。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要分析PoolArena的作用與實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"源碼分析基於Netty 4.1.52"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"接口關係"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ByteBufAllocator,內存分配器,負責爲ByteBuf分配內存, 線程安全。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PooledByteBufAllocator,池化內存分配器,默認的ByteBufAllocator,預先從操作系統中申請一大塊內存,在該內存上分配內存給ByteBuf,可以提高性能和減小內存碎片。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"UnPooledByteBufAllocator,非池化內存分配器,每次都從操作系統中申請內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RecvByteBufAllocator,接收內存分配器,爲Channel讀入的IO數據分配一塊大小合理的buffer空間。具體功能交由內部接口Handle定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它主要是針對Channel讀入場景添加一些操作,如guess,incMessagesRead,lastBytesRead等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ByteBuf,分配好的內存塊,可以直接使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面只關注PooledByteBufAllocator,它是Netty中默認的內存分配器,也是理解Netty內存機制的難點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"內存分配"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面文章《ChannelPipeline機制與讀寫過程》中分析了數據讀取過程,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NioByteUnsafe#read"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public final void read() {\n ...\n final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();\n allocHandle.reset(config);\n\n ByteBuf byteBuf = null;\n\n ...\n byteBuf = allocHandle.allocate(allocator);\n allocHandle.lastBytesRead(doReadBytes(byteBuf));\n ...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"recvBufAllocHandle方法返回AdaptiveRecvByteBufAllocator.HandleImpl。(AdaptiveRecvByteBufAllocator,PooledByteBufAllocator都在DefaultChannelConfig中初始化)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AdaptiveRecvByteBufAllocator.HandleImpl#allocate -> AbstractByteBufAllocator#ioBuffer -> PooledByteBufAllocator#directBuffer -> PooledByteBufAllocator#newDirectBuffer"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {\n // #1\n PoolThreadCache cache = threadCache.get();\n PoolArena directArena = cache.directArena;\n\n final ByteBuf buf;\n if (directArena != null) {\n // #2\n buf = directArena.allocate(cache, initialCapacity, maxCapacity);\n } else {\n // #3\n buf = PlatformDependent.hasUnsafe() ? UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) : new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);\n }\n return toLeakAwareBuffer(buf);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AbstractByteBufAllocator#ioBuffer方法會判斷當前系統是否支持unsafe。支持時使用直接內存,不支持則使用堆內存。這裏只關注直接內存的實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 從當前線程緩存中獲取對應內存池PoolArena"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 在當前線程內存池上分配內存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 內存池不存在,只能使用非池化內存分配內存了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PooledByteBufAllocator#threadCache是一個PoolThreadLocalCache實例,PoolThreadLocalCache繼承於FastThreadLocal,FastThreadLocal這裏簡單理解爲對ThreadLocal的優化,它爲每個線程維護了一個PoolThreadCache,PoolThreadCache上關聯了內存池。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當PoolThreadLocalCache上某個線程的PoolThreadCache不存在時,通過initialValue方法構造。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolThreadLocalCache#initialValue"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"protected synchronized PoolThreadCache initialValue() {\n // #1\n final PoolArena heapArena = leastUsedArena(heapArenas);\n final PoolArena directArena = leastUsedArena(directArenas);\n // #2\n final Thread current = Thread.currentThread();\n if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {\n final PoolThreadCache cache = new PoolThreadCache(\n heapArena, directArena, smallCacheSize, normalCacheSize,\n DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);\n\n ...\n }\n // No caching so just use 0 as sizes.\n return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 從PooledByteBufAllocator的heapArenas,directArenas中獲取使用率最小的PoolArena。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PooledByteBufAllocator構造時默認會爲PooledByteBufAllocator#directArenas初始化8個PoolArena。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 構造PoolThreadCache。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolArena,可以理解爲一個內存池,負責管理從操作系統中申請到的內存塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolThreadCache爲每一個線程關聯一個PoolArena(PoolThreadCache#directArena),該線程的內存都在該PoolArena上分配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Netty支持高併發系統,可能有很多線程進行同時內存分配。爲了緩解線程競爭,通過創建多個PoolArena細化鎖的粒度,從而提高併發執行的效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,一個PoolArena可以會分給多個的線程,可以看到PoolArena上會有一些同步操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"內存級別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面分析SizeClasses的文章說過,Netty將內存池中的內存塊按大小劃分爲3個級別。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同級別的內存塊管理算法不同。默認劃分規則如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"small <= 28672(3.5K)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"normal <= 16777216(2M)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"huge > 16777216(2M)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"smallSubpagePools是一個PoolSubpage數組,負責維護small級別的內存塊信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolChunk負責維護normal級別的內存,PoolChunkList管理一組PoolChunk。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolArena按內存使用率將PoolChunk分別維護到6個PoolChunkList中,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolArena按內存使用率將PoolChunk分別維護到6個PoolChunkList中,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"qInit->內存使用率爲0~25,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"q000->內存使用率爲1~50,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"q025->內存使用率爲25~75,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"q050->內存使用率爲50~75,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"q075->內存使用率爲75~100,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"q100->內存使用率爲100。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:PoolChunk是Netty每次向操作系統申請的內存塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolSubpage需要從PoolChunk中分配,而Tiny,Small級別的內存則是從PoolSubpage中分配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面來看一下分配過程"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private void allocate(PoolThreadCache cache, PooledByteBuf buf, final int reqCapacity) {\n // #1\n final int sizeIdx = size2SizeIdx(reqCapacity);\n // #2\n if (sizeIdx <= smallMaxSizeIdx) {\n tcacheAllocateSmall(cache, buf, reqCapacity, sizeIdx);\n } else if (sizeIdx < nSizes) {\n // #3\n tcacheAllocateNormal(cache, buf, reqCapacity, sizeIdx);\n } else {\n // #4\n int normCapacity = directMemoryCacheAlignment > 0\n ? normalizeSize(reqCapacity) : reqCapacity;\n // Huge allocations are never served via the cache so just call allocateHuge\n allocateHuge(buf, normCapacity);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" size2SizeIdx是父類SizeClasses提供的方法,它使用特定算法,將申請的內存大小調整爲規範大小,劃分到對應位置,返回對應索引,可參考《內存對齊類SizeClasses》"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 分配small級別的內存塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 分配normal級別的內存塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":" 分配huge級別的內存塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private void tcacheAllocateSmall(PoolThreadCache cache, PooledByteBuf buf, final int reqCapacity,\n final int sizeIdx) {\n // #1\n if (cache.allocateSmall(this, buf, reqCapacity, sizeIdx)) {\n return;\n }\n\n // #2\n final PoolSubpage head = smallSubpagePools[sizeIdx];\n final boolean needsNormalAllocation;\n synchronized (head) {\n // #3\n final PoolSubpage s = head.next;\n needsNormalAllocation = s == head;\n if (!needsNormalAllocation) {\n assert s.doNotDestroy && s.elemSize == sizeIdx2size(sizeIdx);\n long handle = s.allocate();\n assert handle >= 0;\n s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity, cache);\n }\n }\n // #4\n if (needsNormalAllocation) {\n synchronized (this) {\n allocateNormal(buf, reqCapacity, sizeIdx, cache);\n }\n }\n\n incSmallAllocation();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 首先嚐試在線程緩存上分配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了PoolArena,PoolThreadCache#smallSubPageHeapCaches還爲每個線程維護了Small級別的內存緩存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 使用前面SizeClasses#size2SizeIdx方法計算的索引,獲取對應PoolSubpage"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 注意,head是一個佔位節點,並不存儲數據,s==head表示當前存在可以用的PoolSubpage,因爲已經耗盡的PoolSubpage是會從鏈表中移除。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着從PoolSubpage中分配內存,後面有文章解析詳細過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,這裏必要運行在同步機制中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":" 沒有可用的PoolSubpage,需要申請一個Normal級別的內存塊,再在上面分配所需內存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"normal級別的內存也是先嚐試在線程緩存中分配,分配失敗後再調用allocateNormal方法申請"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolArena#allocate -> allocateNormal"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private void allocateNormal(PooledByteBuf buf, int reqCapacity, int sizeIdx, PoolThreadCache threadCache) {\n if (q050.allocate(buf, reqCapacity, sizeIdx, threadCache) ||\n q025.allocate(buf, reqCapacity, sizeIdx, threadCache) ||\n q000.allocate(buf, reqCapacity, sizeIdx, threadCache) ||\n qInit.allocate(buf, reqCapacity, sizeIdx, threadCache) ||\n q075.allocate(buf, reqCapacity, sizeIdx, threadCache)) {\n return;\n }\n\n // Add a new chunk.\n PoolChunk c = newChunk(pageSize, nPSizes, pageShifts, chunkSize);\n boolean success = c.allocate(buf, reqCapacity, sizeIdx, threadCache);\n assert success;\n qInit.add(c);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 依次從q050,q025,q000,qInit,q075上申請內存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼要是這個順序呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolArena中的PoolChunkList之間也組成一個“雙向”鏈表"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"qInit ---> q000 q025 q050 q075 q100"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolChunkList中還維護了minUsage,maxUsage,即當一個PoolChunk使用率大於maxUsage,它將被移動到下一個PoolChunkList,使用率小於minUsage,則被移動到前一個PoolChunkList。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:q000沒有前置節點,它的minUsage爲1,即上面的PoolChunk內存完全釋放後,將被銷燬。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"qInit的前置節點是它自己,但它的minUsage爲Integer.MIN_VALUE,即使上面的PoolChunk內存完全釋放後,也不會被銷燬,而是繼續保留在內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不優先從q000分配,正是因爲q000上的PoolChunk內存完全釋放後要被銷燬,如果在上面分配,則會延遲內存的回收進度。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而q075上由於內存利用率太高,導致內存分配的成功率大大降低,因此放到最後。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以從q050是一個不錯的選擇,這樣大部分情況下,Chunk的利用率都會保持在一個較高水平,提高整個應用的內存利用率;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在PoolChunkList上申請內存,PoolChunkList會遍歷鏈表上PoolChunk節點,直到分配成功或到達鏈表末尾。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolChunk分配後,如果內存使用率高於maxUsage,它將被移動到下一個PoolChunkList。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"newChunk方法負責構造一個PoolChunk,這裏是內存池向操作系統申請內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DirectArena#newChunk"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"protected PoolChunk newChunk(int pageSize, int maxPageIdx,\n int pageShifts, int chunkSize) {\n if (directMemoryCacheAlignment == 0) {\n return new PoolChunk(this,\n allocateDirect(chunkSize), pageSize, pageShifts,\n chunkSize, maxPageIdx, 0);\n }\n final ByteBuffer memory = allocateDirect(chunkSize\n + directMemoryCacheAlignment);\n return new PoolChunk(this, memory, pageSize,\n pageShifts, chunkSize, maxPageIdx,\n offsetCacheLine(memory));\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"allocateDirect方法向操作系統申請內存,獲得一個(jvm)ByteBuffer,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PoolChunk#memory維護了該ByteBuffer,PoolChunk的內存實際上都是在該ByteBuffer上分配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後是huge級別的內存申請"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"private void allocateHuge(PooledByteBuf buf, int reqCapacity) {\n PoolChunk chunk = newUnpooledChunk(reqCapacity);\n activeBytesHuge.add(chunk.chunkSize());\n buf.initUnpooled(chunk, reqCapacity);\n allocationsHuge.increment();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比較簡單,沒有使用內存池,直接向操作系統申請內存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"內存釋放"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"void free(PoolChunk chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) {\n if (chunk.unpooled) {\n // #1\n int size = chunk.chunkSize();\n destroyChunk(chunk);\n activeBytesHuge.add(-size);\n deallocationsHuge.increment();\n } else {\n // #2\n SizeClass sizeClass = sizeClass(handle);\n if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {\n // cached so not free it.\n return;\n }\n\n freeChunk(chunk, handle, normCapacity, sizeClass, nioBuffer, false);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 非池化內存,直接銷燬內存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" 池化內存,首先嚐試加到線程緩存中,成功則不需要其他操作。失敗則調用freeChunk"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"void freeChunk(PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass, ByteBuffer nioBuffer,\n boolean finalizer) {\n final boolean destroyChunk;\n synchronized (this) {\n ...\n destroyChunk = !chunk.parent.free(chunk, handle, normCapacity, nioBuffer);\n }\n if (destroyChunk) {\n // destroyChunk not need to be called while holding the synchronized lock.\n destroyChunk(chunk);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"chunk.parent即PoolChunkList,PoolChunkList#free會調用PoolChunk釋放內存,釋放內存後,如果內存使用率低於minUsage,則移動前一個PoolChunkList,如果前一個PoolChunkList不存在(q000),則返回false,由後面的步驟銷燬該PoolChunk。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可回顧前面解析ByteBuf文章中關於內存銷燬的內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您覺得本文不錯,歡迎關注我的微信公衆號,您的關注是我堅持的動力!"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/da/da6c5c8363dc2a6fc148bc2eab39d883.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章