精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

昨天,我徹底搞懂了Netty內存分配策略!

開發(fā) 架構 開發(fā)工具
Netty 作為一款高性能的 RPC 框架必然涉及到頻繁的內存分配銷毀操作。

 Netty 作為一款高性能的 RPC 框架必然涉及到頻繁的內存分配銷毀操作。

[[332042]]

 

圖片來自 Pexels

如果是在堆上分配內存空間將會觸發(fā)頻繁的 GC,JDK 在 1.4 之后提供的 NIO 也已經(jīng)提供了直接直接分配堆外內存空間的能力,但是也僅僅是提供了基本的能力,創(chuàng)建、回收相關的功能和效率都很簡陋。

基于此,在堆外內存使用方面,Netty 自己實現(xiàn)了一套創(chuàng)建、回收堆外內存池的相關功能?;诖宋覀円黄饋砜匆幌?Netty 是如何實現(xiàn)內存分配的。

Netty 中的數(shù)據(jù)容器分類

談到數(shù)據(jù)保存肯定要說到內存分配,按照存儲空間來劃分,可以分為堆內存和堆外內存;按照內存區(qū)域連貫性來劃分可以分為池化內存和非池化內存。這些劃分在 Netty 中的實現(xiàn)接口分別如下。

按照底層存儲空間劃分:

  • 堆緩沖區(qū):HeapBuffer
  • 直接緩沖區(qū):DirectBuffer

按照是否池化劃分:

  • 池化:PooledBuffer
  • 非池化:UnPooledBuffer

默認使用 PoolDireBuf 類型的內存,這些內存主要由 PoolArea 管理。另外 Netty 并不是直接對外暴露這些 API,提供了 Unsafe 類作為出口暴露數(shù)據(jù)分配的相關操作。

什么是池化?

一般申請內存是檢查當前內存哪里有適合當前數(shù)據(jù)塊大小的空閑內存塊,如果有就將數(shù)據(jù)保存在當前內存塊中。

那么池化想做的事情是:既然每次來數(shù)據(jù)都要去找內存地址來存,我就先申請一塊內存地址,這一塊就是我的專用空間,內存分配、回收我全權管理。

池化解決的問題:內存碎片。

內碎片:就是申請的地址空間大于真正數(shù)據(jù)使用的內存空間。

比如固定申請 1M 的空間作為某個線程的使用內存,但是該線程每次最多只占用 0.5M,那么每次都有 0.5M 的碎片。如果該空間不被有效回收時間一長必然存在內存空洞。

外碎片:是指多個內存空間合并的時候發(fā)現(xiàn)不夠分配給待使用的空間大小。

比如有一個 20byte,13byte 的連續(xù)內存空間可以被回收,現(xiàn)在有一個 48byte 的數(shù)據(jù)塊需要存儲,而這兩個加起來也只有 33byte 的空間,必然不會被使用到。

如何實現(xiàn)內存池?

①鏈表維護空閑內存地址

最簡單的就是弄一個鏈表來維護當前空閑的內存空間地址。如果有使用就從鏈表刪除,有釋放就加入鏈表對應位置。

這種方式實現(xiàn)簡單,但是搜索和釋放內存維護的難度還是比較大,不太適合。

②定長內存空間分配

維護兩個列表,一個是未分配內存列表,一個是已分配內存列表。每個內存塊都是一樣大小,分配時如果不夠就將多個塊合并到一起。

這種方式的缺點就是會浪費一定的內存空間,如果有特定的場景還是沒有問題。

③多段定長池分配

在上面的定長分配基礎上,由原來的固定一個長度分配空間變?yōu)榘凑詹煌瑢ο蟠笮?8,16,32,64,128,256,512,1k…64K),的方式分配多個固定大小的內存池。

每次要申請內存的時候按照當前對象大小去對應的池中查找是否有剩余空間。

Linux 本身支持動態(tài)內存分配和釋放,對應的命令為:malloc/free。malloc 的全稱是 memory allocation,中文叫動態(tài)內存分配,用于申請一塊連續(xù)的指定大小的內存塊區(qū)域以 void* 類型返回分配的內存區(qū)域地址。

malloc/free 的實現(xiàn)過程:

  • 空閑存儲空間以空閑鏈表的方式組織(地址遞增),每個塊包含一個長度、一個指向下一塊的指針以及一個指向自身存儲空間的指針。(因為程序中的某些地方可能不通過 malloc 調用申請,因此 malloc 管理的空間不一定連續(xù))
  • 當有申請請求時,malloc 會掃描空閑鏈表,直到找到一個足夠大的塊為止。(首次適應)(因此每次調用 malloc 時并不是花費了完全相同的時間)
  • 如果該塊恰好與請求的大小相符,則將其從鏈表中移走并返回給用戶。如果該塊太大,則將其分為兩部分,尾部的部分分給用戶,剩下的部分留在空閑鏈表中(更改頭部信息)。因此 malloc 分配的是一塊連續(xù)的內存。
  • 釋放時首先搜索空閑鏈表,找到可以插入被釋放塊的合適位置。如果與被釋放塊相鄰的任一邊是一個空閑塊,則將這兩個塊合為一個更大的塊,以減少內存碎片。

Netty 中的內存分配

Netty 采用了 jemalloc 的思想,這是 FreeBSD 實現(xiàn)的一種并發(fā) malloc 的算法。

jemalloc 依賴多個 Arena(分配器)來分配內存,運行中的應用都有固定數(shù)量的多個 Arena,默認的數(shù)量與處理器的個數(shù)有關。

系統(tǒng)中有多個 Arena 的原因是由于各個線程進行內存分配時競爭不可避免,這可能會極大的影響內存分配的效率,為了緩解高并發(fā)時的線程競爭,Netty 允許使用者創(chuàng)建多個分配器(Arena)來分離鎖,提高內存分配效率。

線程首次分配/回收內存時,首先會為其分配一個固定的 Arena。線程選擇 Arena 時使用 round-robin 的方式,也就是順序輪流選取。

每個線程各種保存 Arena 和緩存池信息,這樣可以減少競爭并提高訪問效率。Arena 將內存分為很多 Chunk 進行管理,Chunk 內部保存 Page,以頁為單位申請。

 

申請內存分配時,會將分配的規(guī)格分為如下四類,分別對應不同的范圍,處理過程也不相同:

  • tiny:代表了大小在 0-512B 的內存塊。
  • small:代表了大小在 512B-8K 的內存塊。
  • normal:代表了大小在 8K-16M 的內存塊。
  • huge:代表了大于 16M 的內存塊。

每個塊里面又定義了更細粒度的單位來分配數(shù)據(jù):

  • Chunk:一個 Chunk 的大小是 16M,Chunk 是 Netty 對操作系統(tǒng)進行內存申請的單位,后續(xù)所有的內存分配都是在 Chunk 里面進行操作。
  • Page:Chunk 內部以 Page 為單位分配內存,一個 Page 大小為 8K。當我們需要 16K 的空間時,Netty 就會從一個 Chunk 中找到兩個 Page 進行分配。
  • Subpage 和 element:element 是比 Page 更小的單位,當我們申請小于 8K 的內存時,Netty 會以 element 為單位進行內存分配。element 沒有固定大小,具體由用戶的需求決定。

Netty 通過 Subpage 管理 element,Subpage 是由 Page 轉變過來的。當我們需要 1K 的空間時,Netty 會把一個 Page 變成 Subpage,然后把 Subpage 分成 8 個 1K 的 element 進行分配。

Chunk 中的內存分配

線程分配內存主要從兩個地方:PoolThreadCache 和 Arena。其中 PoolThreadCache 線程獨享,Arena 為幾個線程共享。

 

初次申請內存的時候,Netty 會從一整塊內存(Chunk)中分出一部分來給用戶使用,這部分工作是由 Arena 來完成。

而當用戶使用完畢釋放內存的時候,這些被分出來的內存會按不同規(guī)格大小放在 PoolThreadCache 中緩存起來。當下次要申請內存的時候,就會先從 PoolThreadCache 中找。

Chunk、Page、Subpage 和 element 都是 Arena 中的概念,Arena 的工作就是從一整塊內存中分出合適大小的內存塊。

Arena 中最大的內存單位是 Chunk,這是 Netty 向操作系統(tǒng)申請內存的單位。

而一塊 Chunk(16M) 申請下來之后,內部會被分成 2048 個 Page(8K),當用戶向 Netty 申請超過 8K 內存的時候,Netty 會以 Page 的形式分配內存。

Chunk 內部通過伙伴算法管理 Page,具體實現(xiàn)為一棵完全平衡二叉樹:

二叉樹中所有子節(jié)點管理的內存也屬于其父節(jié)點。當我們要申請大小為 16K 的內存時,我們會從根節(jié)點開始不斷尋找可用的節(jié)點,一直到第 10 層。

 

那么如何判斷一個節(jié)點是否可用呢?Netty 會在每個節(jié)點內部保存一個值,這個值代表這個節(jié)點之下的第幾層還存在未分配的節(jié)點。

比如第 9 層的節(jié)點的值如果為 9,就代表這個節(jié)點本身到下面所有的子節(jié)點都未分配。

如果第 9 層的節(jié)點的值為 10,代表它本身不可被分配,但第 10 層有子節(jié)點可以被分配。

如果第 9 層的節(jié)點的值為 12,此時可分配節(jié)點的深度大于了總深度,代表這個節(jié)點及其下面的所有子節(jié)點都不可被分配。

下圖描述了分配的過程:

 

對于小內存(小于 4096)的分配還會將 Page 細化成更小的單位 Subpage。

Subpage 按大小分有兩大類:

  • Tiny:小于 512 的情況,最小空間為 16,對齊大小為 16,區(qū)間為[16,512),所以共有 32 種情況。
  • Small:大于等于 512 的情況,總共有四種:512,1024,2048,4096。

PoolSubpage 中直接采用位圖管理空閑空間(因為不存在申請 k 個連續(xù)的空間),所以申請釋放非常簡單。

第一次申請小內存空間的時候,需要先申請一個空閑頁,然后將該頁轉成 PoolSubpage,再將該頁設為已被占用,最后再把這個 PoolSubpage 存到 PoolSubpage 池中。

這樣下次就不需要再去申請空閑頁了,直接去池中找就好了。Netty 中有 36 種 PoolSubpage,所以用 36 個 PoolSubpage 鏈表表示 PoolSubpage 池。

因為單個 PoolChunk 只有 16M,這遠遠不夠用,所以會很很多很多 PoolChunk,這些 PoolChunk 組成一個鏈表,然后用 PoolChunkList 持有這個鏈表。

我們先從內存分配器 PoolArena 來分析 Netty 中的內存是如何分配的,Area 的工作就是從一整塊內存中協(xié)調如何分配合適大小的內存給當前數(shù)據(jù)使用。

PoolArena 是 Netty 的內存池實現(xiàn)抽象類,其內部子類為 HeapArena 和 DirectArena。

HeapArena 對應堆內存(heap buffer),DirectArena 對應堆外直接內存(direct buffer),兩者除了操作的內存(byte[] 和 ByteBuffer)不同外其余完全一致。

從結構上來看,PoolArena 中主要包含三部分子內存池:

  • tinySubpagePools
  • smallSubpagePools
  • 一系列的 PoolChunkList

tinySubpagePools 和 smallSubpagePools 都是 PoolSubpage 的數(shù)組,數(shù)組長度分別為 32 和 4。

PoolChunkList 是一個容器,其內部可以保存一系列的 PoolChunk 對象,并且,Netty 會根據(jù)內存使用率的不同,將 PoolChunkList 分為不同等級的容器。

  1. abstract class PoolArena<T> implements PoolArenaMetric { 
  2.  
  3.    enum SizeClass { 
  4.         Tiny, 
  5.         Small, 
  6.         Normal 
  7.     } 
  8.   // 該參數(shù)指定了tinySubpagePools數(shù)組的長度,由于tinySubpagePools每一個元素的內存塊差值為16, 
  9.     // 因而數(shù)組長度是512/16,也即這里的512 >>> 4 
  10.   static final int numTinySubpagePools = 512 >>> 4; 
  11.     //表示該PoolArena的allocator 
  12.   final PooledByteBufAllocator parent; 
  13.   //表示PoolChunk中由Page節(jié)點構成的二叉樹的最大高度,默認11 
  14.   private final int maxOrder; 
  15.   //page的大小,默認8K 
  16.   final int pageSize; 
  17.   // 指定了葉節(jié)點大小8KB是2的多少次冪,默認為13,該字段的主要作用是,在計算目標內存屬于二叉樹的 
  18.     // 第幾層的時候,可以借助于其內存大小相對于pageShifts的差值,從而快速計算其所在層數(shù) 
  19.   final int pageShifts; 
  20.   //默認16MB 
  21.   final int chunkSize; 
  22.   // 由于PoolSubpage的大小為8KB=8196,因而該字段的值為 
  23.     // -8192=>=> 1111 1111 1111 1111 1110 0000 0000 0000 
  24.     // 這樣在判斷目標內存是否小于8KB時,只需要將目標內存與該數(shù)字進行與操作,只要操作結果等于0, 
  25.     // 就說明目標內存是小于8KB的,這樣就可以判斷其是應該首先在tinySubpagePools或smallSubpagePools 
  26.     // 中進行內存申請 
  27.   final int subpageOverflowMask; 
  28.   // 該參數(shù)指定了smallSubpagePools數(shù)組的長度,默認為4 
  29.   final int numSmallSubpagePools; 
  30.   //tinySubpagePools用來分配小于512 byte的Page 
  31.   private final PoolSubpage<T>[] tinySubpagePools; 
  32.   //smallSubpagePools用來分配大于等于512 byte且小于pageSize內存的Page 
  33.   private final PoolSubpage<T>[] smallSubpagePools; 
  34.   //用來存儲用來分配給大于等于pageSize大小內存的PoolChunk 
  35.   //存儲內存利用率50-100%的chunk 
  36.   private final PoolChunkList<T> q050; 
  37.   //存儲內存利用率25-75%的chunk 
  38.   private final PoolChunkList<T> q025; 
  39.   //存儲內存利用率1-50%的chunk 
  40.   private final PoolChunkList<T> q000; 
  41.   //存儲內存利用率0-25%的chunk 
  42.   private final PoolChunkList<T> qInit; 
  43.   //存儲內存利用率75-100%的chunk 
  44.   private final PoolChunkList<T> q075; 
  45.   //存儲內存利用率100%的chunk 
  46.   private final PoolChunkList<T> q100; 
  47.     //堆內存(heap buffer) 
  48.   static final class HeapArena extends PoolArena<byte[]> { 
  49.  
  50.   } 
  51.    //堆外直接內存(direct buffer) 
  52.   static final class DirectArena extends PoolArena<ByteBuffer> { 
  53.  
  54.   } 
  55.  
  56.  

如上所示,PoolArena 是由多個 PoolChunk 組成的大塊內存區(qū)域,而每個 PoolChunk 則由多個 Page 組成。

當需要分配的內存小于 Page 的時候,為了節(jié)約內存采用 PoolSubpage 實現(xiàn)小于 Page 大小內存的分配。

在 PoolArena 中為了保證 PoolChunk 空間的最大利用化,按照 PoolArena 中各 個 PoolChunk 已使用的空間大小將其劃分為六類:

  • qInit:存儲內存利用率 0-25% 的 chunk
  • q000:存儲內存利用率 1-50% 的 chunk
  • q025:存儲內存利用率 25-75% 的 chunk
  • q050:存儲內存利用率 50-100% 的 chunk
  • q075:存儲內存利用率 75-100%的 chunk
  • q100:存儲內存利用率 100%的 chunk

PoolArena 維護了一個 PoolChunkList 組成的雙向鏈表,每個 PoolChunkList 內部維護了一個 PoolChunk 雙向鏈表。

分配內存時,PoolArena 通過在 PoolChunkList 找到一個合適的 PoolChunk,然后從 PoolChunk 中分配一塊內存。

下面來看 PoolArena 是如何分配內存的:

  1. private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) { 
  2.   // 將需要申請的容量格式為 2^N 
  3.   final int normCapacity = normalizeCapacity(reqCapacity); 
  4.   // 判斷目標容量是否小于8KB,小于8KB則使用tiny或small的方式申請內存 
  5.   if (isTinyOrSmall(normCapacity)) { // capacity < pageSize 
  6.     int tableIdx; 
  7.     PoolSubpage<T>[] table
  8.     boolean tiny = isTiny(normCapacity); 
  9.     // 判斷目標容量是否小于512字節(jié),小于512字節(jié)的為tiny類型的 
  10.     if (tiny) { // < 512 
  11.       // 將分配區(qū)域轉移到 tinySubpagePools 中 
  12.       if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) { 
  13.         // was able to allocate out of the cache so move on 
  14.         return
  15.       } 
  16.       // 如果無法從當前線程緩存中申請到內存,則嘗試從tinySubpagePools中申請,這里tinyIdx()方法 
  17.       // 就是計算目標內存是在tinySubpagePools數(shù)組中的第幾號元素中的 
  18.       tableIdx = tinyIdx(normCapacity); 
  19.       table = tinySubpagePools; 
  20.     } else { 
  21.       // 如果目標內存在512byte~8KB之間,則嘗試從smallSubpagePools中申請內存。這里首先從 
  22.       // 當前線程的緩存中申請small級別的內存,如果申請到了,則直接返回 
  23.       if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) { 
  24.         // was able to allocate out of the cache so move on 
  25.         return
  26.       } 
  27.       tableIdx = smallIdx(normCapacity); 
  28.       table = smallSubpagePools; 
  29.     } 
  30.         // 獲取目標元素的頭結點 
  31.     final PoolSubpage<T> head = table[tableIdx]; 
  32.  
  33.     // 這里需要注意的是,由于對head進行了加鎖,而在同步代碼塊中判斷了s != head, 
  34.     // 也就是說PoolSubpage鏈表中是存在未使用的PoolSubpage的,因為如果該節(jié)點已經(jīng)用完了, 
  35.     // 其是會被移除當前鏈表的。也就是說只要s != head,那么這里的allocate()方法 
  36.     // 就一定能夠申請到所需要的內存塊 
  37.     synchronized (head) { 
  38.       // s != head就證明當前PoolSubpage鏈表中存在可用的PoolSubpage,并且一定能夠申請到內存, 
  39.       // 因為已經(jīng)耗盡的PoolSubpage是會從鏈表中移除的 
  40.       final PoolSubpage<T> s = head.next
  41.       // 如果此時 subpage 已經(jīng)被分配過內存了執(zhí)行下文,如果只是初始化過,則跳過該分支 
  42.       if (s != head) { 
  43.         // 從PoolSubpage中申請內存 
  44.         assert s.doNotDestroy && s.elemSize == normCapacity; 
  45.         // 通過申請的內存對ByteBuf進行初始化 
  46.         long handle = s.allocate(); 
  47.         assert handle >= 0; 
  48.         // 初始化 PoolByteBuf 說明其位置被分配到該區(qū)域,但此時尚未分配內存 
  49.         s.chunk.initBufWithSubpage(buf, handle, reqCapacity); 
  50.                 // 對tiny類型的申請數(shù)進行更新 
  51.         if (tiny) { 
  52.           allocationsTiny.increment(); 
  53.         } else { 
  54.           allocationsSmall.increment(); 
  55.         } 
  56.         return
  57.       } 
  58.     } 
  59.     // 走到這里,說明目標PoolSubpage鏈表中無法申請到目標內存塊,因而就嘗試從PoolChunk中申請 
  60.     allocateNormal(buf, reqCapacity, normCapacity); 
  61.     return
  62.   } 
  63.    // 走到這里說明目標內存是大于8KB的,那么就判斷目標內存是否大于16M,如果大于16M, 
  64.   // 則不使用內存池對其進行管理,如果小于16M,則到PoolChunkList中進行內存申請 
  65.   if (normCapacity <= chunkSize) { 
  66.     // 小于16M,首先到當前線程的緩存中申請,如果申請到了則直接返回,如果沒有申請到, 
  67.     // 則到PoolChunkList中進行申請 
  68.     if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) { 
  69.       // was able to allocate out of the cache so move on 
  70.       return
  71.     } 
  72.     allocateNormal(buf, reqCapacity, normCapacity); 
  73.   } else { 
  74.     // 對于大于16M的內存,Netty不會對其進行維護,而是直接申請,然后返回給用戶使用 
  75.     allocateHuge(buf, reqCapacity); 
  76.   } 

所有內存分配的 size 都會經(jīng)過 normalizeCapacity() 進行處理,申請的容量總是會被格式為 2^N。

主要規(guī)則如下:

  • 如果目標容量小于 16 字節(jié),則返回 16。
  • 如果目標容量大于 16 字節(jié),小于 512 字節(jié),則以 16 字節(jié)為單位,返回大于目標字節(jié)數(shù)的第一個 16 字節(jié)的倍數(shù)。比如申請的 100 字節(jié),那么大于 100 的 16 整數(shù)倍最低為:16*7=112,因而返回 112。
  • 如果目標容量大于 512 字節(jié),則返回大于目標容量的第一個 2 的指數(shù)冪。比如申請的 1000 字節(jié),那么返回的將是:2^10 = 1024。

PoolArena 提供了兩種方式進行內存分配:

①PoolSubpage 用于分配小于 8k 的內存

tinySubpagePools:用于分配小于 512 字節(jié)的內存,默認長度為 32,因為內存分配最小為 16,每次增加 16,直到 512,區(qū)間 [16,512) 一共有 32 個不同值。

smallSubpagePools:用于分配大于等于 512 字節(jié)的內存,默認長度為 4。tinySubpagePools 和 smallSubpagePools 中的元素默認都是 subpage。

②poolChunkList 用于分配大于 8k 的內存

上面已經(jīng)解釋了 q 開頭的幾個變量用于保存大于 8k 的數(shù)據(jù)。

默認先嘗試從 poolThreadCache 中分配內存,PoolThreadCache 利用 ThreadLocal 的特性,消除了多線程競爭,提高內存分配效率;

首次分配時,poolThreadCache 中并沒有可用內存進行分配,當上一次分配的內存使用完并釋放時,會將其加入到 poolThreadCache 中,提供該線程下次申請時使用。

如果是分配小內存,則嘗試從 tinySubpagePools 或 smallSubpagePools 中分配內存,如果沒有合適 subpage,則采用方法 allocateNormal 分配內存。

如果分配一個 page 以上的內存,直接采用方法 allocateNormal() 分配內存,allocateNormal() 則會將申請動作交由 PoolChunkList 進行。

  1. private synchronized void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) { 
  2.   //如果在對應的PoolChunkList能申請到內存,則返回 
  3.   if (q050.allocate(buf, reqCapacity, normCapacity) || q025.allocate(buf, reqCapacity, normCapacity) || 
  4.       q000.allocate(buf, reqCapacity, normCapacity) || qInit.allocate(buf, reqCapacity, normCapacity) || 
  5.       q075.allocate(buf, reqCapacity, normCapacity)) { 
  6.     ++allocationsNormal; 
  7.     return
  8.   } 
  9.  
  10.   // Add a new chunk. 
  11.   PoolChunk<T> c = newChunk(pageSize, maxOrder, pageShifts, chunkSize); 
  12.   long handle = c.allocate(normCapacity); 
  13.   ++allocationsNormal; 
  14.   assert handle > 0; 
  15.   c.initBuf(buf, handle, reqCapacity); 
  16.   qInit.add(c); 

首先將申請動作按照 q050→q025→q000→qInit→q075 的順序依次交由各個 PoolChunkList 進行處理,如果在對應的 PoolChunkList 中申請到了內存,則直接返回。

如果申請不到,那么直接創(chuàng)建一個新的 PoolChunk,然后在該 PoolChunk 中申請目標內存,最后將該 PoolChunk 添加到 qInit 中。

上面說過 Chunk 是 Netty 向操作系統(tǒng)申請內存塊的最大單位,每個 Chunk 是 16M。

PoolChunk 內部通過 memoryMap 數(shù)組維護了一顆完全平衡二叉樹作為管理底層內存分布及回收的標記位,所有的子節(jié)點管理的內存也屬于其父節(jié)點。

關于 PoolChunk 內部如何維護完全平衡二叉樹就不在這里展開,大家有興趣可以自行看源碼。

對于內存的釋放,PoolArena 主要是分為兩種情況,即池化和非池化,如果是非池化,則會直接銷毀目標內存塊,如果是池化的,則會將其添加到當前線程的緩存中。

如下是 free() 方法的源碼:

  1. public void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacity, 
  2.      PoolThreadCache cache) { 
  3.   // 如果是非池化的,則直接銷毀目標內存塊,并且更新相關的數(shù)據(jù) 
  4.   if (chunk.unpooled) { 
  5.     int size = chunk.chunkSize(); 
  6.     destroyChunk(chunk); 
  7.     activeBytesHuge.add(-size); 
  8.     deallocationsHuge.increment(); 
  9.   } else { 
  10.     // 如果是池化的,首先判斷其是哪種類型的,即tiny,small或者normal, 
  11.     // 然后將其交由當前線程的緩存進行處理,如果添加成功,則直接返回 
  12.     SizeClass sizeClass = sizeClass(normCapacity); 
  13.     if (cache != null && cache.add(this, chunk, nioBuffer, handle, 
  14.           normCapacity, sizeClass)) { 
  15.       return
  16.     } 
  17.  
  18.     // 如果當前線程的緩存已滿,則將目標內存塊返還給公共內存塊進行處理 
  19.     freeChunk(chunk, handle, sizeClass, nioBuffer); 
  20.   } 

作者:rickiyang

編輯:陶家龍

出處:轉載自微信公眾號 rickiyang

責任編輯:武曉燕 來源: rickiyang
相關推薦

2020-10-14 08:50:38

搞懂 Netty 線程

2025-06-30 00:32:43

策略模式算法MyBatis

2023-06-16 14:10:00

TCPUDP網(wǎng)絡通信

2021-06-30 08:45:02

內存管理面試

2010-09-17 16:14:22

Java內存分配

2021-03-23 10:25:05

Redis數(shù)據(jù)結構

2025-09-08 04:11:00

2018-04-08 08:45:53

對象內存策略

2020-06-09 08:19:25

微服務網(wǎng)站架構

2025-04-21 04:00:00

2024-12-03 08:16:57

2025-07-15 10:41:44

2024-10-16 07:58:48

2024-09-23 09:12:20

2024-07-17 09:32:19

2025-05-28 02:20:00

2019-10-30 21:27:51

Java中央處理器電腦

2020-09-11 07:38:50

內存泄漏檢測

2023-12-11 11:29:35

2025-08-21 01:45:00

Linux內存對齊
點贊
收藏

51CTO技術棧公眾號

成人综合在线观看| 狠狠干成人综合网| 69久久99精品久久久久婷婷 | 亚洲一级淫片| 精品美女一区二区| 国产视频一区二区三区在线播放| 日韩在线资源| www.亚洲免费av| 国产精品av电影| 欧美精品一区二区成人| 久久不见久久见中文字幕免费| 欧美欧美欧美欧美首页| 黄页网站大全在线观看| 91在线看黄| av在线不卡观看免费观看| 国产男女猛烈无遮挡91| 久久久午夜影院| 婷婷久久综合| 亚洲人成自拍网站| 成人在线观看一区二区| 成人性片免费| 欧美视频中文字幕在线| 国产高清免费在线| 激情小视频在线| 成人18视频日本| 成人黄色av播放免费| 亚洲国产成人精品激情在线| 亚洲美女视频| 中文字幕一区电影| 国产真实乱人偷精品人妻| 成人看片爽爽爽| 制服丝袜亚洲播放| 91网址在线播放| 正在播放日韩精品| 亚洲精品成人悠悠色影视| 日韩三级电影免费观看| 亚洲av永久无码国产精品久久| 美女视频免费一区| 热99精品里视频精品| 久久精品性爱视频| 中文字幕一区二区三区久久网站| 日韩在线观看网站| 国产精品天天干| 自拍视频一区| 精品视频久久久久久久| 一级黄色电影片| 1204国产成人精品视频| 欧美一卡2卡3卡4卡| 在线免费黄色网| 亚洲网站三级| 777欧美精品| 搡的我好爽在线观看免费视频| 国产成人精品一区二区三区免费| 91电影在线观看| 国产超碰在线播放| 日本另类视频| 欧美日韩高清在线播放| 一级黄色特级片| 日韩av懂色| 91精品久久久久久蜜臀| 欧美又黄又嫩大片a级| 日本国产一区| 91精品国产欧美一区二区18| 永久免费看片在线观看| 看亚洲a级一级毛片| 日韩亚洲欧美一区| 91传媒理伦片在线观看| 精品国产影院| 国产午夜精品全部视频播放| 任你操精品视频| 欧美在线免费| 高清欧美性猛交| 亚洲第一网站在线观看| 美女看a上一区| 91精品国产高清久久久久久91裸体| 99精品视频免费看| 成人爽a毛片一区二区免费| 精品蜜桃一区二区三区| 国产系列在线观看| 亚洲视频 欧洲视频| 青草网在线观看| 美女100%一区| 欧美精品在线视频| 亚洲一级Av无码毛片久久精品| 亚洲成在人线免费观看| 中文字幕亚洲欧美| 久久久久久欧美精品se一二三四| 99精品视频免费| 国产精品一区二区女厕厕| av片免费播放| 久久久久久免费毛片精品| 一区二区视频国产| sm久久捆绑调教精品一区| 在线观看欧美日本| 日本中文字幕精品| 成人激情视频| 97精品国产97久久久久久免费| 午夜精品免费观看| 国产不卡视频一区二区三区| 欧美激情第六页| 香蕉久久aⅴ一区二区三区| 五月激情六月综合| 超碰人人草人人| 香蕉久久精品日日躁夜夜躁| 久久久久www| 久久精品视频5| 国产成人在线影院| 日本一区二区三区精品视频| 羞羞的视频在线观看| 在线免费一区三区| 在线免费看黄色片| 婷婷综合五月| 国产精品久久久久久亚洲影视 | 蜜桃视频一区二区在线观看| 久久野战av| 亚洲电影中文字幕| 黄色一级大片在线免费观看| 巨乳诱惑日韩免费av| 国产伦理一区二区三区| 午夜激情视频在线| 一本色道久久综合狠狠躁的推荐| 久久久久亚洲av无码网站| 91日韩视频| 国产z一区二区三区| 欧美 日韩 国产 精品| 亚洲日本在线a| 日本肉体xxxx裸体xxx免费| 天堂俺去俺来也www久久婷婷 | 床上的激情91.| 一区二区三区我不卡| 91国拍精品国产粉嫩亚洲一区| 亚洲国产高清自拍| 国产性生活网站| 国产精品 欧美精品| 一区二区在线不卡| 欧美日一区二区三区| 亚洲一级免费视频| 91精品国产高清一区二区三密臀| av在线不卡免费看| 欧美一级视频免费看| 2020国产精品极品色在线观看| 久久影院免费观看| 国产理论视频在线观看| 国产精品久久久久久久岛一牛影视| 日本成人中文字幕在线| 免费观看久久av| 国产ts人妖一区二区三区| 日本大臀精品| 91久久精品一区二区三| 免费看污片的网站| 久久亚洲不卡| 色涩成人影视在线播放| 成人高清一区| 播播国产欧美激情| 国产欧美久久久精品免费| 亚洲欧美偷拍三级| 91视频福利网| 精品69视频一区二区三区Q| 国产精品伊人日日| 中文在线最新版地址| 精品无人国产偷自产在线| 亚洲熟妇无码乱子av电影| 国产清纯白嫩初高生在线观看91| 无码少妇一区二区三区芒果| 久久美女视频| 3d精品h动漫啪啪一区二区| 国产白丝在线观看| 日韩av资源在线播放| 日本黄色一级视频| 中文字幕一区二区三区av| 欧美熟妇另类久久久久久多毛| 影音先锋久久| 欧洲在线视频一区| 国产精品亚洲成在人线| 久久99视频免费| 亚洲人成色777777精品音频| 91黄色在线观看| 熟女少妇a性色生活片毛片| 国产精品 欧美精品| 黄色片视频在线免费观看| 久久国产精品亚洲人一区二区三区| 91久久精品国产| 黄色视屏在线免费观看| 在线看日韩欧美| 亚洲国产www| 91精品1区2区| 国产一级片播放| 欧美国产丝袜视频| 亚洲色图欧美另类| 日韩高清欧美激情| 日本大片免费看| 日韩一区欧美| 久久国产精品一区二区三区| 欧美风情在线视频| 97国产精品久久| 成人看片免费| 伊人精品在线观看| 神马午夜精品95| 欧美日韩国产色站一区二区三区| 久久亚洲精品大全| 国产精品成人一区二区三区夜夜夜| 黄色国产在线视频| 久久99日本精品| 青青草原成人网| 欧美特黄a级高清免费大片a级| 欧美影视一区二区| 久久精品66| 亚洲伊人成综合成人网| av在线日韩| 9.1国产丝袜在线观看| 18+激情视频在线| 亚洲最大在线视频| 青青青手机在线视频观看| 日韩视频免费直播| 亚洲最新av网站| 色婷婷久久久综合中文字幕| 日本一二三区视频| 樱花影视一区二区| 很污很黄的网站| 国产欧美日韩在线观看| 国产精品一区二区入口九绯色| 国产精品资源站在线| 超碰在线公开97| 久久一本综合频道| 男人用嘴添女人下身免费视频| 综合久久综合| 男人的天堂成人| 欧美国产一区二区三区激情无套| 日韩亚洲一区在线播放| 伊人成综合网伊人222| 精品国产一区二区三区四区vr| 亚洲一区二区三区在线免费| 3d动漫啪啪精品一区二区免费 | 免费人成在线不卡| 亚洲色欲综合一区二区三区| 99国产精品| 青青青免费在线| 日韩视频三区| 玩弄中年熟妇正在播放| 亚洲激情黄色| 免费看日本毛片| 国产一区二区三区的电影| 欧美视频在线免费播放| 99在线精品视频在线观看| www.日本在线播放| 一本久道久久综合狠狠爱| 毛片在线视频播放| 久久xxxx精品视频| 国产成人精品视频ⅴa片软件竹菊| 久久久久国产精品午夜一区| 久久久久久香蕉| 男女男精品视频网| 性欧美1819| 国产精品亚洲成人| 欧美xxxxx少妇| 久久亚洲一级片| 精品日韩在线视频| 中文字幕中文在线不卡住| 国产福利视频网站| 亚洲高清中文字幕| 成人免费视频毛片| 在线视频综合导航| 国产又粗又长又大视频| 欧美一区二区三区电影| 懂色av一区二区三区四区| 亚洲精品久久久久久下一站| 你懂得网站在线| 中文字幕一精品亚洲无线一区| 成人看av片| 2019亚洲男人天堂| 朝桐光一区二区| 91久热免费在线视频| 国产精品毛片av| 日本一区视频在线观看| 你懂的成人av| 我的公把我弄高潮了视频| 日韩国产精品91| 永久看看免费大片| 99久久久免费精品国产一区二区| 免费一级做a爰片久久毛片潮| 国产精品久久久久久久浪潮网站| 精品97人妻无码中文永久在线| 亚洲妇女屁股眼交7| 波多野结衣午夜| 欧美一卡2卡3卡4卡| 欧美在线一卡| 欧美成年人网站| 成人私拍视频| αv一区二区三区| 在线亚洲a色| www.欧美黄色| 日韩精品1区2区3区| 四虎国产精品免费| 久久精品亚洲精品国产欧美kt∨ | 96pao国产成视频永久免费| 国产精品巨作av| www.午夜色| 国产精品呻吟| 欧美人与性动交α欧美精品| 久久久精品免费网站| 欧美成人三级在线观看| 91黄视频在线观看| 深爱五月激情五月| 久久久999成人| 国产精品极品美女在线观看| 99国产高清| 久久久久美女| 国产精品亚洲二区在线观看| 国产凹凸在线观看一区二区| 亚洲a∨无码无在线观看| 天天操天天色综合| 国产日韩精品suv| 中文字幕亚洲欧美日韩2019| 日本蜜桃在线观看视频| 91精品国产高清久久久久久91裸体| 精品毛片免费观看| 欧美亚洲一二三区| 成人免费视频播放| 精品欧美一区二区久久久久| 欧美视频中文一区二区三区在线观看| 天天摸天天干天天操| 欧美激情久久久久久| a一区二区三区亚洲| 性欧美.com| 日日骚欧美日韩| 亚洲av综合一区二区| 精品久久久久久久久久久久| www日本在线| 久久精品国产91精品亚洲| 成人一区视频| 亚洲精品中文字幕乱码三区不卡| 亚洲一区二区网站| 亚洲av成人精品一区二区三区| 亚洲伦理在线精品| 一级黄色片在线| 最近2019中文字幕mv免费看 | 欧美日韩大陆一区二区| 国产精品视频一区二区久久| 国产精品国产三级国产专播精品人| 日韩中文av| 97超碰青青草| 久久综合国产精品| 日韩免费视频一区二区视频在线观看| 亚洲精品mp4| 色一区二区三区| 麻豆91蜜桃| 日韩激情视频网站| 精品成人无码一区二区三区| 91搞黄在线观看| 日本三级在线播放完整版| 成人a在线视频| 亚洲激情中文| 女性生殖扒开酷刑vk| 黄色成人在线免费| 天堂a中文在线| 国产mv久久久| 色88久久久久高潮综合影院| 天天做天天干天天操| 亚洲免费观看高清完整版在线观看熊 | 国产精品二区三区| 黄色av日韩| 免费a级黄色片| 欧洲国内综合视频| 麻豆影院在线| 99理论电影网| 欧美亚洲自偷自偷| 国产一区在线观看免费| 91精品国产入口| 成年人黄色大片在线| 日韩精品久久一区二区三区| 美女久久久精品| 久久久美女视频| 亚洲精品自拍第一页| 国产精品.xx视频.xxtv| 可以在线看黄的网站| www.日韩av| 中文区中文字幕免费看| 欧美成人午夜影院| 美国十次av导航亚洲入口| 国产精品igao| 亚洲激情校园春色| 日韩有码电影| 91在线观看免费高清| 99在线精品视频在线观看| 国产成人免费在线观看视频| 日韩欧美在线综合网| 手机在线理论片| 国产成年人在线观看| 99久久国产免费看| 国产一区二区视频免费观看| 97精品免费视频| 久久人人88| 国产中文字幕一区二区| 538prom精品视频线放| 亚洲福利影院| 亚洲性无码av在线| 久久久无码精品亚洲国产| 亚洲国产mv| 美女三级黄色片| 日韩成人av一区|