一、JDKByteBuffer的缺點
1.無法動態擴容:長度是固定,不能動態擴展和收縮,當數據大於ByteBuffer容量時,會發生索引越界異常。
2.API使用複雜:讀寫的時候需要手工調用flip()和rewind()等方法,使用時需要非常謹慎的使用這些api,否則很容易出現錯誤。
二、ByteBuf做的增強方面
1.API操作便捷性
2.動態擴容
3.多種ByteBuf實現
4.高效的零拷貝機制
三、netty中對ByteBuf操作
ByteBuf三個重要的屬性:capacity容量、readerIndex讀取位置、writeindex寫入位置。提供了兩個指針變量來支持順序讀和寫操作,分別爲readerIndex和寫操作writeIndex。
代碼實例:
public class ByteBufDemo {
@Test
public void apiTest() {
// +-------------------+------------------+------------------+
// | discardable bytes | readable bytes | writable bytes |
// | | (CONTENT) | |
// +-------------------+------------------+------------------+
// | | | |
// 0 <= readerIndex <= writerIndex <= capacity
// 1.創建一個非池化的ByteBuf,大小爲10個字節
ByteBuf buf = Unpooled.buffer(10);
System.out.println("原始ByteBuf爲====================>" + buf.toString());
System.out.println("1.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 2.寫入一段內容
byte[] bytes = {1, 2, 3, 4, 5};
buf.writeBytes(bytes);
System.out.println("寫入的bytes爲====================>" + Arrays.toString(bytes));
System.out.println("寫入一段內容後ByteBuf爲===========>" + buf.toString());
System.out.println("2.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 3.讀取一段內容
byte b1 = buf.readByte();
byte b2 = buf.readByte();
System.out.println("讀取的bytes爲====================>" + Arrays.toString(new byte[]{b1, b2}));
System.out.println("讀取一段內容後ByteBuf爲===========>" + buf.toString());
System.out.println("3.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 4.將讀取的內容丟棄
buf.discardReadBytes();
System.out.println("將讀取的內容丟棄後ByteBuf爲========>" + buf.toString());
System.out.println("4.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 5.清空讀寫指針
buf.clear();
System.out.println("將讀寫指針清空後ByteBuf爲==========>" + buf.toString());
System.out.println("5.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 6.再次寫入一段內容,比第一段內容少
byte[] bytes2 = {1, 2, 3};
buf.writeBytes(bytes2);
System.out.println("寫入的bytes爲====================>" + Arrays.toString(bytes2));
System.out.println("寫入一段內容後ByteBuf爲===========>" + buf.toString());
System.out.println("6.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
// 7.將ByteBuf清零
buf.setZero(0, buf.capacity());
System.out.println("將內容清零後ByteBuf爲==============>" + buf.toString());
System.out.println("7.ByteBuf中的內容爲================>" + Arrays.toString(buf.array()) + "\n");
// 8.再次寫入一段超過容量的內容
byte[] bytes3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
buf.writeBytes(bytes3);
System.out.println("寫入的bytes爲====================>" + Arrays.toString(bytes3));
System.out.println("寫入一段內容後ByteBuf爲===========>" + buf.toString());
System.out.println("8.ByteBuf中的內容爲===============>" + Arrays.toString(buf.array()) + "\n");
}
四、動態擴容
capacity默認值:256字節、最大值:Integer.MAX_VALUE(2GB);write*方法調用時,通過AbstractByteBuf.ensureWritable()進行檢查。容量計算方法:AbstractByteBufAllocator.calculateNewCapacity(新capacity的最小要求,capacity最大值)根據capacity的最小值要求,對應有兩套計算方法:
1.沒超過4兆:從64字節開始,每次增加一倍,直至計算出來的newCapacity滿足新容量最小要求。實例:當前大小256,已寫250,繼續寫10字節數據,需要的容量最小要求是261,則新容量是64*2*2*2 = 512。
2.超過4兆:新容量 = 新容量最小要求/4兆 * 4兆 + 4兆。實例:當前大小3兆,已寫3兆,繼續寫2兆數據,需要的容量最小要求是5兆,則新容量是9兆(不能超過最大值)。
五、豐富的byteBuf實現
netty中根據3個緯度,可以劃分出8中byteBuf的實現:
堆外/堆內 | 是否池化 | 訪問方式 | 具體實現類 | 備註 |
heap堆內 | unpool | safe | UnpooledHeapByteBuf | 數組實現 |
unsafe | UnpooledUnsafeHeapByteBuf | unsafe類直線操作內存 | ||
pool | safe | PooledHeapByteBuf | ||
unsafe | PooledUnsafeHeapByteBuf | |||
direct堆外 | unpool | safe | UnpooledDirectByteBuf | NIO DirectByteBuffer |
unsafe | UnpooledUnsafeDirectByteBuf | |||
pool | safe | PooledDirectByteBuf | ||
unsafe | PooledUnsafeDirectByteBuf |
在使用中,都是通過ByteBufAllocator分配器進行申請,同時分配器具備有內存管理的功能。
六、零拷貝機制
netty的零拷貝機制,是一種應用層的實現。和底層JVM、操作系統內存機制並無過多關聯。
- CompositeByteBuf,將多個ByteBuf合併爲一個邏輯上的ByteBuf,避免了各個ByteBuf之間的拷貝。
- wrapedBuffer()方法,將byte[]數組包裝成ByteBuf對象
- slice()方法。將一個ByteBuf對象切分成多個ByteBuf對象。