Netty ByteBuf源碼分析

Netty的ByteBuf是JDK中ByteBuffer的升級版,提供了NIO buffer和byte數組的抽象視圖。

ByteBuf的主要類集成關係:

(圖片來自Netty權威指南,圖中有一個畫錯的地方是PooledByteBuf中的最後一個子類應該是PooledUnsafeDirectByteBuf)


從繼承關係可以看出AbstractReferenceCountedByteBuf的子類分爲兩類:Pooled和Unpooled的ByteBuf。

Pooled ByteBuf是基於對象池的ByteBuf(會被緩存),Unpooled則是普通的ByteBuf對象。

無論是否基於內存池的ByteBuf,它的子類有分爲DirectByteBuf和HeapByteBuf分別表示在堆外內存分配的緩衝區和在堆內存的緩衝區。

堆內存緩衝區的特點是分配和回收速度快,可以被JVM自動回收;缺點是如果進行Socket的IO讀寫,需要額外做一次內存複製。

堆外內存的緩衝區相對於堆內存的緩衝區,分配和回收的速度會慢一些,但是將它寫入或從Socket Channel讀取數據是,會少一次內存複製,速度比堆內存更快一些。

BufferBuf也提供了一類API來將自己轉化成爲ByteBuffer以便於在需要ByteBuffer的地方進行操作。


AbstractByteBuf

AbstractByteBuf定義了ByteBuf的基礎屬性和一些公用的方法。

主要成員變量有:

readerIndex

writerIndex

markedReaderIndex

markedWriterIndex

maxCapacity

可以看到ByteBuf中分別定義了讀寫遊標,也就是說讀遊標和寫遊標是分離的,這相對於ByteBuffer只使用一個position,在讀寫操作時會便利很多。


 


一塊緩衝區會被兩個遊標分隔爲三塊區域,分別是已經讀取過的、可讀的、剩餘可寫的。

在寫入數據後可以直接開始讀取,不需要flip操作。

 

另外AbstractByteBuf中的方法分爲三類:

讀取數據、寫入數據、操作遊標

 

讀取操作分爲readXXX和getXXX,以讀取Long爲例,

readLong()方法在當前readerIndex位置讀取8個Byte並增加readerindex的值;

getLong(int index)則需要接收一個index參數,從index位置開始讀取8個Byte,get操作不會改變readerIndex的值。



寫入操作也有兩類,分別是setXXX和writeXXX,

writeLong(long value)方法在當前writerIndex位置開始寫入一個Long值並增加writerIndex的值;

setLong(int index, long value)方法則接收一個index參數作爲寫入的位置,寫入操作不修改writerIndex的值。

索引操作就是通過setReaderIndex之類的方法改變readerIndex的值來重新讀取數據,非常簡單,不做說明。


 

AbstractReferenceCountedByteBuf

從類名上看,AbstractReferenceCountedByteBuf主要是實現了對引用的計數,類似於JVM內存回收的對象引用計數器,用於跟蹤對象的分配和銷燬。



只有一個成員變量:refCnt,看命名也知道是用於計數的,並且用AtomicIntegerFieldUpdater來實現線程安全的計數。 

retain API用於增加引用的計數。

release則用於減少計數值。

定義deallocate方法讓子類去實現當引用計數爲0時的操作(由子類實現具體的“回收”操作)。

 

UnpooledHeapByteBuffer

UnpooledHeapByteBuffer是不適用對象池,且直接在堆內存上分配的緩衝區。 


ByteBufAllocator alloc:創建了這個緩衝區的分配器

byte[] array:底層存儲

ByteBuffer tmpNioBuf:Nio  ByteBuffer對象,用於將自身轉換成一個ByteBuffer對象



構造方法非常簡單:

記錄分配器alloc;

初始化數組;

將讀寫位置調整爲0;


這個構造方法則是傳入byte數組做初始化,writerIndex調整到數組後的第一個位置。

兩個構造方法都有maxCapacity參數,且初始容量要麼是initialCapacity要麼是initalArray的長度,說明緩衝區的大小是可以調整的,最大不超過maxCapacity。


通過capacity來調整緩衝區的大小。如果newCapacity小於當前array的length,那麼會有部分數據被丟棄。如果newCapacity超過array的length,那麼新擴容的位置數據未空。

 

UnpooledDirectByteBuf


ByteBufAllocator alloc:使用的分配器

ByteBuffer buffer:內部用於存儲數據的結構

ByteBuffer tmpNioBuf:臨時的ByteBuffer,在將自己轉換成ByteBuffer對象時會使用

int capacity:容量

doNotFree:標誌ByteBuffer是否可以回收


區別於UnpooledHeapByteBuf,UnpooledDirectByteBuf的擴容操作不在是申請數組進行數據拷貝,而是申請新的ByteBuffer之後進行收拷貝,而ByteBuffer一定是Direct的。


deallocate實現了底層存儲ByteBuffer的回收操作,即在引用計數爲0時將DirectByteBuffer回收掉(DirectByteBuffer的內存是由自己回收的,而不是JVM)。

 

UnpooledUnsafeDirectByteBuf

UnpooledUnsafeDirectByteBuf和UnpooledUnsafeDirectByteBuf的區別在於UnpooledUnsafeDirectByteBuf直接使用ByteBuffer來操作數據,而UnpooledUnsafeDirectByteBuf採用Unsafe來操作數據。

UnpooledUnsafeDirectByteBuf的_getLong實現:

UnpooledUnsafeDirectByteBuf的_getLong實現:


 

AbstractReferenceCountedByteBuf子類的另一個分支是PooledByteBuf,即使用對象池,會被緩存的ByteBuffer。 

PooledByteBuf有三個子類:

PooledHeapByteBuf

PooledDirectByteBuf

PooledUnsafeDirectByteBuf

他們分別和Unpooled的幾個子類對應。

 

對於Pooled的實現,詳見Netty對象池實現分析。


本文轉自:h t t p : / / ww w . c nb l o g s. c o m/ h z m ar k / p/ N e tt y _ By t e B uf . h t ml

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章