#頭條創作挑戰賽#
九、ByteBuf淺層複制的進階使用方式
淺層複制是一種非常重要的操作。可以很大程度地避免記憶體複制。這一點對于大規模消息通信來說是非常重要的。
ByteBuf的淺層複制分為兩種,有切片(slice)淺層複制和整體(duplicate)淺層複制。
slice切片淺層複制
ByteBuf的slice方法可以擷取到一個ByteBuf的一個切片。一個ByteBuf可以進行多次的切片淺層複制;多次切片後的ByteBuf對象可以共享一個存儲區域。
示例:
package com.crazymakercircle.netty.bytebuf;
//....
public class SliceTest {
@Test
public void testSlice() {
ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(9, 100);
print("動作:配置設定ByteBuf(9, 100)", buffer);
buffer.writeBytes(new byte[]{1, 2, 3, 4});
print("動作:寫入4個位元組 (1,2,3,4)", buffer);
ByteBuf slice = buffer.slice();
print("動作:切片slice", slice);
}
}
在上面代碼中,輸出了源ByteBuf和調用slice方法後的切片ByteBuf的三組屬性值,運作結果如下:
//…篇幅原因,省略了ByteBuf剛配置設定後的屬性值輸出
[main|SliceTest:print]:after ===========動作:寫入4個位元組
(1,2,3,4)============
[main|SliceTest:print]:1.0 isReadable(): true
[main|SliceTest:print]:1.1 readerIndex(): 0
[main|SliceTest:print]:1.2 readableBytes(): 4
[main|SliceTest:print]:2.0 isWritable(): true
[main|SliceTest:print]:2.1 writerIndex(): 4
[main|SliceTest:print]:2.2 writableBytes(): 5
[main|SliceTest:print]:3.0 capacity(): 9
[main|SliceTest:print]:3.1 maxCapacity(): 100
[main|SliceTest:print]:3.2 maxWritableBytes(): 96
[main|SliceTest:print]:after ===========動作:切片slice============
[main|SliceTest:print]:1.0 isReadable(): true
[main|SliceTest:print]:1.1 readerIndex(): 0
[main|SliceTest:print]:1.2 readableBytes(): 4
[main|SliceTest:print]:2.0 isWritable(): false
[main|SliceTest:print]:2.1 writerIndex(): 4
[main|SliceTest:print]:2.2 writableBytes(): 0
[main|SliceTest:print]:3.0 capacity(): 4
[main|SliceTest:print]:3.1 maxCapacity(): 4
[main|SliceTest:print]:3.2 maxWritableBytes(): 0
調用slice()方法後,傳回的切片是一個新的ByteBuf對象,該對象的幾個重要屬性值,大緻如下:
· readerIndex(讀指針)的值為0。
· writerIndex(寫指針)的值為源Bytebuf的readableBytes()可讀位元組數。
· maxCapacity(最大容量)的值為源Bytebuf的readableBytes( )可讀位元組數。
切片後的新Bytebuf有兩個特點:
· 切片不可以寫入,原因是:maxCapacity與writerIndex值相同。
· 切片和源ByteBuf的可讀位元組數相同,原因是:切片後的可讀位元組數為自己的屬性writerIndex - readerIndex,也就是源ByteBuf的readableBytes()-0。
切片後的新ByteBuf和源ByteBuf的關聯性:
· 切片不會複制源ByteBuf的底層資料,底層數組和源ByteBuf的底層數組是同一個。
· 切片不會改變源ByteBuf的引用計數。
從根本上說,slice()無參數方法所生成的切片就是源ByteBuf可讀部分的淺層複制。
duplicate整體淺層複制
和slice切片不同,duplicate() 傳回的是源ByteBuf的整個對象的一個淺層複制,包括如下内容:
· duplicate的讀寫指針、最大容量值,與源ByteBuf的讀寫指針相同。
· duplicate() 不會改變源ByteBuf的引用計數。
· duplicate() 不會複制源ByteBuf的底層資料。
duplicate() 和slice() 方法都是淺層複制。不同的是,slice()方法是切取一段的淺層複制,而duplicate( )是整體的淺層複制。
淺層複制的問題
淺層複制方法不會實際去複制資料,也不會改變ByteBuf的引用計數,這就會導緻一個問題:在源ByteBuf調用release() 之後,一旦引用計數為零,就變得不能通路了;在這種場景下,源ByteBuf的所有淺層複制執行個體也不能進行讀寫了;如果強行對淺層複制執行個體進行讀寫,則會報錯。
是以,在調用淺層複制執行個體時,可以通過調用一次retain() 方法來增加引用,表示它們對應的底層記憶體多了一次引用,引用計數為2。在淺層複制執行個體用完後,需要調用兩次release()方法,将引用計數減一,這樣就不影響源ByteBuf的記憶體釋放。