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