netty源碼解析(4.0)-23 ByteBuf內存管理:分配和釋放


  ByteBuf內存分配和釋放由具體實現負責,抽象類型只定義的內存分配和釋放的時機。

 

  內存分配分兩個階段: 第一階段,初始化時分配內存。第二階段: 內存不夠用時分配新的內存。ByteBuf抽象層沒有定義第一階段的行為,但定義了第二階段的方法:

  public abstract ByteBuf capacity(int newCapacity)

  這個方法負責分配一個長度為newCapacity的新內存。

 

  內存釋放的抽象實現在AbstractReferenceCountedByteBuf中實現,這個類實現引用計數,當調用release方法把引用計數變成0時,會調用

  protected abstract void deallocate()

  執行真正的內存釋放操作。

 

內存相關的屬性

  ByteBuf定義了兩個內存相關的屬性:

  capacity: 當前的當前的容量,也就是當前使用的內存大小。使用capacity()方法獲得。

  maxCapacity: 最大容量,也就是可以使用的最大內存大小。使用newCapacity()方法獲得。

 

內存分配時機

  AbstractByteBuf定義了內存分配的時機。當writeXX方法被調用的時候,如果如果發現可寫空間不足,就調用capacity分配新的內存。下面以writeInt為例詳細分析這個過程。

 1     @Override
 2     public ByteBuf writeInt(int value) {
 3         ensureWritable0(4);
 4         _setInt(writerIndex, value);
 5         writerIndex += 4;
 6         return this;
 7     }    
 8 
 9 
10     final void ensureWritable0(int minWritableBytes) {
11         ensureAccessible();
12         if (minWritableBytes <= writableBytes()) {
13             return;
14         }
15 
16         if (minWritableBytes > maxCapacity - writerIndex) {
17             throw new IndexOutOfBoundsException(String.format(
18                     "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
19                     writerIndex, minWritableBytes, maxCapacity, this));
20         }
21 
22         // Normalize the current capacity to the power of 2.
23         int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);
24 
25         // Adjust to the new capacity.
26         capacity(newCapacity);
27     }
28         

  3行: ensureWritable確保當前能寫入4Byte的數據。

  11行: 確保當前ByteBuf是可以訪問的。防止多線程環境下,ByteBuf內存被釋放后讀寫數據。

  12,13行: 如果內存夠用,就此作罷。

  16行:  如果需要內存大於maxCapacity拋出異常。

  23, 26行行: 計算新內存的大小, 調用capacity(int)分配新內存。

  

  重新分配內存之前的一個重要步驟的計算新內存的大小。這個工作由calculateNewCapacity方法完成,它的代碼如下:

 1     private int calculateNewCapacity(int minNewCapacity) {
 2         final int maxCapacity = this.maxCapacity;
 3         final int threshold = 1048576 * 4; // 4 MiB page
 4 
 5         if (minNewCapacity == threshold) {
 6             return threshold;
 7         }
 8 
 9         // If over threshold, do not double but just increase by threshold.
10         if (minNewCapacity > threshold) {
11             int newCapacity = minNewCapacity / threshold * threshold;
12             if (newCapacity > maxCapacity - threshold) {
13                 newCapacity = maxCapacity;
14             } else {
15                 newCapacity += threshold;
16             }
17             return newCapacity;
18         }
19 
20         // Not over threshold. Double up to 4 MiB, starting from 64.
21         int newCapacity = 64;
22         while (newCapacity < minNewCapacity) {
23             newCapacity <<= 1;
24         }
25 
26         return Math.min(newCapacity, maxCapacity);
27     }

  1行:接受一個最小的新內存參數minNewCapacity。

  3行: 定義一個4MB的閾值常量threshold。

  5,6行: 如果minNewCapacity==threshold,那么新內存大小就是threshold。

  10-17行: 如果minNewCapacity>threshold, 新內存大小是min(maxCapacity, threshold * n)且threshold * n >= minNewCapacity。

  21-26行: 如果minNewCapacity<threshold, 新內存大小是min(maxCapacity, 64 * 2n)且64 * 2n >= minNewCapacity。

 

內存分配和釋放的具體實現

  本章涉及到的內存分配和釋放的具體實現只涉及到unpooled類型的ByteBuf,即:

  UnpooledHeapByteBuf

  UnpooledDirectByteBuf

  UnpooledUnsafeHeapByteBuf

  UnpooledUnsafeDirectByteBuf

  這幾個具體實現中涉及到的內存分配和釋放代碼比較簡潔,更容易明白ByteBuf內存管理的原理。相比之下,pooled類型的ByteBuf內存分配和釋放的代碼要復雜很多,會在后面的章節獨立分析。

  

  UnpooledHeapByteBuf和UnpooledUnsafeHeapByteBuf實現

   UnpooledHeapByteBuf中,內存分配的實現代碼主要集中在capacity(int)和allocateArray()方法中。capacity分配新內存的步驟是

  • 調用allocateArray分配一塊新內存。
  • 把舊內存中的實際復制到新內存中。
  • 使用新內存替換舊內存(24行)。
  • 釋放掉舊內存(25行)。

   代碼如下:

 1     @Override
 2     public ByteBuf capacity(int newCapacity) {
 3         checkNewCapacity(newCapacity);
 4 
 5         int oldCapacity = array.length;
 6         byte[] oldArray = array;
 7         if (newCapacity > oldCapacity) {
 8             byte[] newArray = allocateArray(newCapacity);
 9             System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
10             setArray(newArray);
11             freeArray(oldArray);
12         } else if (newCapacity < oldCapacity) {
13             byte[] newArray = allocateArray(newCapacity);
14             int readerIndex = readerIndex();
15             if (readerIndex < newCapacity) {
16                 int writerIndex = writerIndex();
17                 if (writerIndex > newCapacity) {
18                     writerIndex(writerIndex = newCapacity);
19                 }
20                 System.arraycopy(oldArray, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
21             } else {
22                 setIndex(newCapacity, newCapacity);
23             }
24             setArray(newArray);
25             freeArray(oldArray);
26         }
27         return this;
28     }
29     

  capacity中復制舊內存數據到新內存中的時候分兩種情況(newCapacity,oldCapacity分別是新舊內存的大小):

  • newCapacity>oldCapacity,這種情況比較簡單,直接復制就好了(第8行)。不影響readerIndex和writerIndex。
  • newCapacity<oldCapacity,  這種情況比較復雜。capacity盡量把可讀的數據復制新內存中。
    • 如果readerIndex<newCapacity且writerIndex<newCapacity。可讀數據會全部轉移到新內存中。readerIndex和writerIndex保持不變。
    • 如果readerIndex<newCapacity且writeIndex>newCapacity。可端數據會部分轉移的新內存中,會丟失部分可讀數據。readerIndex不變,writerIndex變成newCapacity。
    • 如果readerIndex>newCapacity,數據全部丟失,readerIndex和writerIndex都會變成newCapacity。

   allocateArray方法負責分配一塊新內存,它的實現是new byte[]。freeArray方法負責釋放內存,這個方法是個空方法。

  UnpooledUnsafeHeapByteBuf是UnloopedHeadpByteBuf的直接子類,在內存管理上的差別是allocateArray的實現,UnpooledUnsafeHeapByteBuf的實現是:  

1     @Override
2     byte[] allocateArray(int initialCapacity) {
3         return PlatformDependent.allocateUninitializedArray(initialCapacity);
4     }

 

  UnpooledDirectByteBuf和UnpooledUnsafeDirectByteBuf實現

  UnpooledDirectByteBuf類使用DirectByteBuffer作為內存,使用了DirectByteBuffer的能力來實現ByteBuf接口。allocateDirect和freeDirect方法負責分配和釋放DirectByteBuffer。capacity(int)方法和UnloopedHeapByteBuf類似,使用allocateDirect創建一個新的DirectByteBuffer, 把舊內存數據復制到新內存中,然后使用新內存替換舊內存,最后調用freeDirect方法釋放掉舊的DirectByteBuffer。

 1     protected ByteBuffer allocateDirect(int initialCapacity) {
 2         return ByteBuffer.allocateDirect(initialCapacity);
 3     }
 4 
 5     protected void freeDirect(ByteBuffer buffer) {
 6         PlatformDependent.freeDirectBuffer(buffer);
 7     }
 8 
 9   @Override
10     public ByteBuf capacity(int newCapacity) {
11         checkNewCapacity(newCapacity);
12 
13         int readerIndex = readerIndex();
14         int writerIndex = writerIndex();
15 
16         int oldCapacity = capacity;
17         if (newCapacity > oldCapacity) {
18             ByteBuffer oldBuffer = buffer;
19             ByteBuffer newBuffer = allocateDirect(newCapacity);
20             oldBuffer.position(0).limit(oldBuffer.capacity());
21             newBuffer.position(0).limit(oldBuffer.capacity());
22             newBuffer.put(oldBuffer);
23             newBuffer.clear();
24             setByteBuffer(newBuffer);
25         } else if (newCapacity < oldCapacity) {
26             ByteBuffer oldBuffer = buffer;
27             ByteBuffer newBuffer = allocateDirect(newCapacity);
28             if (readerIndex < newCapacity) {
29                 if (writerIndex > newCapacity) {
30                     writerIndex(writerIndex = newCapacity);
31                 }
32                 oldBuffer.position(readerIndex).limit(writerIndex);
33                 newBuffer.position(readerIndex).limit(writerIndex);
34                 newBuffer.put(oldBuffer);
35                 newBuffer.clear();
36             } else {
37                 setIndex(newCapacity, newCapacity);
38             }
39             setByteBuffer(newBuffer);
40         }
41         return this;
42     }

  對比UnloopedHeapByteBuf的capacity(int)方法,發現這兩個實現非常類似,也分兩種情況處理:

  • 18-24行,newCapacity > oldCapacity的情況。
  • 26-39行, newCapacity < oldCapacity的情況。

  兩種情況對readerIndex和writerIndex的影響也一樣,不同的是數據復制時的具體實現。  

  UnpooledUnsafeDirectByteBuf和UnpooledDirectByteBuf同屬於AbstractReferenceCountedByteBuf的派生類,它們之間沒有繼承關系。但內存分配和釋放實現是一樣的,不同的地方是內存I/O。UnpooledUnsafeDirectByteBuf使用UnsafeByteBufUtil類之間讀寫DirectByteBuffer的內存,沒有使用DirectByteBuffer的I/O能力。

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM