bytebuf池_图文分析ByteBuf是什么
ByteBuf是什么
ByteBuf是Netty中非常重要的一個(gè)組件,他就像物流公司的運(yùn)輸工具:卡車,火車,甚至是飛機(jī)。而物流公司靠什么盈利,就是靠運(yùn)輸貨物,可想而知ByteBuf在Netty中是多么的重要。沒有了ByteBuf,Netty就失去了靈魂,其他所有的都將變得毫無意義。
ByteBuf是由Byte和Buffer兩個(gè)詞組合成的一個(gè)詞,但是因?yàn)镴DK中已經(jīng)有了一個(gè)ByteBuffer,并且使用非常復(fù)雜,API及其不友好,可謂是千夫所指。為了扭轉(zhuǎn)ByteBuffer在大家心目中的形象,Netty重新設(shè)計(jì)了一個(gè)ByteBuffer,即 ByteBuf。
從字面上我們可以知道 ByteBuf 是處理字節(jié)的,并且還有一種緩沖的能力。
ByteBuf在官方中是這樣定義的:
A random and sequential accessible sequence of zero or more bytes (octets).
This interface provides an abstract view for one or more primitive byte
arrays ({@code byte[]}) and {@linkplain ByteBuffer NIO buffers}.
就是說 ByteBuf 是一個(gè)字節(jié)序列,可以隨機(jī)或連續(xù)存取零到多個(gè)字節(jié)。他提供了一個(gè)統(tǒng)一的抽象,通過 ByteBuf 可以操作基礎(chǔ)的字節(jié)數(shù)組和ByteBuffer緩沖區(qū)。
需要注意的是這里說的 interface 是不準(zhǔn)確的,因?yàn)門rustin Lee在2013/7/8將ByteBuffer從接口改成了抽象類,具體的原因不得而知。
ByteBuf的結(jié)構(gòu)
ByteBuf比JDK中原生的ByteBuffer好的原因是前者的設(shè)計(jì)比后者優(yōu)秀,ByteBuf有讀和寫兩個(gè)指針,而ByteBuffer只有一個(gè)指針,需要通過flip()方法在讀和寫之間進(jìn)行模式切換,需要操作的越多往往犯錯(cuò)的概率就越大。ByteBuf將讀和寫進(jìn)行了分離,使用者不用再關(guān)心現(xiàn)在是讀還是寫的模式,可以把更多的精力用在具體的業(yè)務(wù)上。
官方定義中指出,ByteBuf主要是通過兩個(gè)指針進(jìn)行數(shù)據(jù)的讀和寫,分別是 readerIndex 和 writerIndex ,并且整個(gè)ByteBuf被這兩個(gè)指針最多分成三個(gè)部分,分別是可丟棄部分,可讀部分和可寫部分,可以用一張圖直觀的描述ByteBuf的結(jié)構(gòu),如下圖所示:
可能有人注意到了我說ByteBuf最多被分成三個(gè)部分,那是因?yàn)槟承┣闆r下可能只有一到兩部分:
剛初始化的時(shí)候
剛初始化的時(shí)候,讀寫指針都是0,所有的內(nèi)容都是可寫部分,此時(shí)還沒有可讀部分和可丟棄部分。
剛寫完數(shù)據(jù)后
剛寫完一些數(shù)據(jù)后,讀指針仍然是0,寫指針向后移動(dòng)了n,這里的n就是寫入的字節(jié)數(shù)。
讀完一部分?jǐn)?shù)據(jù)并丟棄之后
寫入完數(shù)據(jù)之后,緊接著讀取一部分?jǐn)?shù)據(jù),然后立刻丟棄掉,此時(shí)ByteBuf的結(jié)構(gòu)就會(huì)變成跟第二步中的一樣。因?yàn)閬G棄的動(dòng)作會(huì)將讀指針向左移動(dòng)到0的位置,寫指針向左移動(dòng)的距離=原來讀指針的值
ByteBuf的讀寫操作
寫操作
ByteBuf中定義了兩類方法可以往ByteBuf中寫入內(nèi)容:writeXX() 和 setXX()。
具體的setXX()類的方法可以用下面的一張表格來描述:
方法名
描述
setByte(int index, int value)
將指定位置上的內(nèi)容修改為指定的byte的值
高24位上的內(nèi)容將被丟棄
setBoolean(int index, boolean value)
將指定位置上的內(nèi)容修改為指定的boolean的值
setBytes(int index,byte src)
將指定的字節(jié)內(nèi)容
可以從byte[],ByteBuf,ByteBuffer,InputStream,Channel等中獲取
轉(zhuǎn)移到指定的位置
setChar*(int index, int value)
將指定位置上的內(nèi)容修改為指定的character的UTF-16編碼下2-byte的值
高16位上的內(nèi)容將被丟棄
setShort*(int index, int value)
將指定位置上的內(nèi)容修改為指定的integer的低16-bit的值
高16位上的內(nèi)容將被丟棄
setMidium*(int index, int value)
將指定位置上的內(nèi)容修改為指定的integer的中間24-bit的值
大多數(shù)重要的內(nèi)容將被丟棄
setInt*(int index, int value)
將指定位置上的內(nèi)容修改為指定的32-bit的integer的值
setFloat*(int index, float value)
將指定位置上的內(nèi)容修改為指定的32-bit的float的值
setDouble*(int index, double value)
將指定位置上的內(nèi)容修改為指定的64-bit的float的值
setLong*(int index, long value)
將指定位置上的內(nèi)容修改為指定的64-bit的long的值
setZero(int index, int length)
將從指定位置index開始之后的length個(gè)長(zhǎng)度的值設(shè)置為0x00
我們知道java中一個(gè)int占4個(gè)字節(jié),即32bit,一個(gè)short占2個(gè)字節(jié),一個(gè)int可以拆成2個(gè)short,所以就會(huì)存在當(dāng)寫入一個(gè)short時(shí),參數(shù)用int來傳值時(shí),高16位的內(nèi)容會(huì)被丟棄。這是因?yàn)橐粋€(gè)int被拆成了兩個(gè)short,而寫入一個(gè)short到指定的位置時(shí),那么另一個(gè)short就被丟棄了,且是高16位的這個(gè)short。
有的人注意到了上面好多方法后面都有*,這是表示這些方法還有一種兄弟方法,如setInt對(duì)應(yīng)的是setIntLE,這表示以小端字節(jié)序的方式寫入內(nèi)容。簡(jiǎn)單來說一般網(wǎng)絡(luò)傳輸采用大端字節(jié)序,另外我們?nèi)祟悓懽止?jié)的順序也是大端字節(jié)序,而計(jì)算機(jī)處理字節(jié)的順序一般是小端字節(jié)序(但是也不絕對(duì),計(jì)算機(jī)從低電平開始讀取字節(jié)時(shí)效率更高),具體什么是大端字節(jié)序,什么是小端字節(jié)序不是本篇文章深入研究的范圍,大家可以自行查閱有關(guān)資料。
PS:需要注意的是如果寫入的位置index小于0,或者index加上寫入內(nèi)容的值超過capcity的話,會(huì)拋出 IndexOutOfBoundsException,所以就存在兩個(gè)比較重要的方法:isWritable(),isReadable(),他們將返回當(dāng)前ByteBuf中是否還有足夠的空間可以寫和可以讀
具體的writeXX()方法與上面的setXX()方法類似,不同的是writeXX()方法會(huì)更新寫指針,即向ByteBuf中寫入具體的內(nèi)容后,writeIndex會(huì)向后移動(dòng)與寫入的內(nèi)容字節(jié)數(shù)長(zhǎng)度相同的距離。
讀操作
跟寫操作一樣,ByteBuf的讀操作也有兩種方法,分別是getXX()和readXX()。
讀操作包含的具體方法與寫操作也是一一對(duì)應(yīng)的,具體的可以把上面的那張表格中的set改為get,并且將第二個(gè)value參數(shù)移除即可,例如:getShort(int index),getInt(int index)等等。
與getXX()方法相關(guān)的另一類方法就是readXX()方法了,與get方法不同的是,read方法會(huì)更改讀指針的值。
ByteBuf的種類
我們知道ByteBuf在4.x的版本中是一個(gè)抽象類,他有很多的抽象子類以及各種實(shí)現(xiàn)類。
畫了一個(gè)簡(jiǎn)單的ByteBuf的各個(gè)實(shí)現(xiàn)類之間的關(guān)系,其中藍(lán)色的類是被棄用的。
上圖只是簡(jiǎn)單的列舉的一些常用的ByteBuf類,如果你想知道ByteBuf所有的實(shí)現(xiàn)類,那么可以在IDEA中選
則ByteBuf類之后,然后在菜單 navigate 中點(diǎn)擊 Type Hierarchy 或用快捷鍵:control+H,即可打開ByteBuf的類層次結(jié)構(gòu)圖,具體的層級(jí)結(jié)構(gòu)如下圖所示:
本篇文章只簡(jiǎn)單的讓大家對(duì)于ByteBuf的種類有個(gè)大概的了解,具體的每一種ByteBuf的作用我將在后續(xù)的章節(jié)中進(jìn)行介紹。
ByteBuf的使用
有一點(diǎn)我們需要知道的是,ByteBuf的jar包,是可以單獨(dú)使用的。比如某個(gè)項(xiàng)目中有一個(gè)場(chǎng)景,需要處理某個(gè)自定義的協(xié)議,那么我們?cè)诮馕鰠f(xié)議時(shí),就可以將接收到的將字節(jié)內(nèi)容寫入一個(gè)ByteBuf,然后從ByteBuf中慢慢的將內(nèi)容讀取出來。下面讓我們用一個(gè)例子簡(jiǎn)單的了解下ByteBuf的使用。
ByteBuf的創(chuàng)建
要想使用ByteBuf,首先肯定是要?jiǎng)?chuàng)建一個(gè)ByteBuf,更確切的說法就是要申請(qǐng)一塊內(nèi)存,后續(xù)可以在這塊內(nèi)存中執(zhí)行寫入數(shù)據(jù)讀取數(shù)據(jù)等等一系列的操作。
那么如何創(chuàng)建一個(gè)ByteBuf呢?Netty中設(shè)計(jì)了一個(gè)專門負(fù)責(zé)分配ByteBuf的接口:ByteBufAllocator。該接口有一個(gè)抽象子類和兩個(gè)實(shí)現(xiàn)類,分別對(duì)應(yīng)了用來分配池化的ByteBuf和非池化的ByteBuf。
具體的層級(jí)關(guān)系如下圖所示:
有了Allocator之后,Netty又為我們提供了兩個(gè)工具類:Pooled、Unpooled,分類用來分配池化的和未池化的ByteBuf,進(jìn)一步簡(jiǎn)化了創(chuàng)建ByteBuf的步驟,只需要調(diào)用這兩個(gè)工具類的靜態(tài)方法即可。
不同的創(chuàng)建方法
我們以Unpooled類為例,查看Unpooled的源碼可以發(fā)現(xiàn),他為我們提供了許多創(chuàng)建ByteBuf的方法,但最終都是以下這幾種,只是參數(shù)不一樣而已:
// 在堆上分配一個(gè)ByteBuf,并指定初始容量和最大容量
public static ByteBuf buffer(int initialCapacity, int maxCapacity) {
return ALLOC.heapBuffer(initialCapacity, maxCapacity);
}
// 在堆外分配一個(gè)ByteBuf,并指定初始容量和最大容量
public static ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
return ALLOC.directBuffer(initialCapacity, maxCapacity);
}
// 使用包裝的方式,將一個(gè)byte[]包裝成一個(gè)ByteBuf后返回
public static ByteBuf wrappedBuffer(byte[] array) {
if (array.length == 0) {
return EMPTY_BUFFER;
}
return new UnpooledHeapByteBuf(ALLOC, array, array.length);
}
// 返回一個(gè)組合ByteBuf,并指定組合的個(gè)數(shù)
public static CompositeByteBuf compositeBuffer(int maxNumComponents){
return new CompositeByteBuf(ALLOC, false, maxNumComponents);
}
其中包裝方法除了上述這個(gè)方法之外,還有一些其他常用的包裝方法,比如參數(shù)是一個(gè)ByteBuf的包裝方法,比如參數(shù)是一個(gè)原生的ByteBuffer的包裝方法,比如指定一個(gè)內(nèi)存地址和大小的包裝方法等等。
另外還有一些copy*開頭的方法,實(shí)際是調(diào)用了buffer(int initialCapacity, int maxCapacity)或directBuffer(int initialCapacity, int maxCapacity)方法,然后將具體的內(nèi)容write進(jìn)生成的ByteBuf中返回。
以上所有的這些方法都實(shí)際通過一個(gè)叫ALLOC的靜態(tài)變量進(jìn)行了調(diào)用,來實(shí)現(xiàn)具體的ByteBuf的創(chuàng)建,而這個(gè)ALLOC實(shí)際是一個(gè)ByteBufAllocator:
private static final ByteBufAllocator
ALLOC = UnpooledByteBufAllocator.DEFAULT;
ByteBufAllocator是一個(gè)專門負(fù)責(zé)ByteBuf分配的接口,對(duì)應(yīng)的Unpooled實(shí)現(xiàn)類就是UnpooledByteBufAllocator。在UnpooledByteBufAllocator類中可以看到UnpooledByteBufAllocator.DEFAULT變量是一個(gè)final類型的靜態(tài)變量
/**
* Default instance which uses leak-detection for direct buffers.
* 默認(rèn)的UnpooledByteBufAllocator實(shí)例,并且會(huì)對(duì)堆外內(nèi)存進(jìn)行泄漏檢測(cè)
*/
public static final UnpooledByteBufAllocator
DEFAULT = new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
涉及的設(shè)計(jì)模式
ByteBuf和ByteBufAllocator之間是一種相輔相成的關(guān)系,ByteBufAllocator用來創(chuàng)建一個(gè)ByteBuf,而ByteBuf亦可以返回創(chuàng)建他的Allocator。ByteBuf和ByteBufAllocator之間是一種 抽象工廠模式,具體可以用一張圖描述如下:
下面我來用一個(gè)實(shí)際的例子來說明ByteBuf的使用,并通過觀察在不同階段ByteBuf的讀寫指針的值和ByteBuf的容量變化來更加深入的了解ByteBuf的設(shè)計(jì),為了方便,我會(huì)用非池化的分配器來創(chuàng)建ByteBuf。
使用示例
我構(gòu)造了一個(gè)demo,來演示在ByteBuf中插入數(shù)據(jù)、讀取數(shù)據(jù)、清空讀寫指針、數(shù)據(jù)清零、擴(kuò)容等等方法,具體的代碼如下:
private static void simpleUse(){
// 1.創(chuàng)建一個(gè)非池化的ByteBuf,大小為10個(gè)字節(jié)
ByteBuf buf = Unpooled.buffer(10);
System.out.println("原始ByteBuf為====================>"+buf.toString());
System.out.println("1.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 2.寫入一段內(nèi)容
byte[] bytes = {1,2,3,4,5};
buf.writeBytes(bytes);
System.out.println("寫入的bytes為====================>"+Arrays.toString(bytes));
System.out.println("寫入一段內(nèi)容后ByteBuf為===========>"+buf.toString());
System.out.println("2.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 3.讀取一段內(nèi)容
byte b1 = buf.readByte();
byte b2 = buf.readByte();
System.out.println("讀取的bytes為====================>"+Arrays.toString(new byte[]{b1,b2}));
System.out.println("讀取一段內(nèi)容后ByteBuf為===========>"+buf.toString());
System.out.println("3.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 4.將讀取的內(nèi)容丟棄
buf.discardReadBytes();
System.out.println("將讀取的內(nèi)容丟棄后ByteBuf為========>"+buf.toString());
System.out.println("4.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 5.清空讀寫指針
buf.clear();
System.out.println("將讀寫指針清空后ByteBuf為==========>"+buf.toString());
System.out.println("5.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 6.再次寫入一段內(nèi)容,比第一段內(nèi)容少
byte[] bytes2 = {1,2,3};
buf.writeBytes(bytes2);
System.out.println("寫入的bytes為====================>"+Arrays.toString(bytes2));
System.out.println("寫入一段內(nèi)容后ByteBuf為===========>"+buf.toString());
System.out.println("6.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
// 7.將ByteBuf清零
buf.setZero(0,buf.capacity());
System.out.println("將內(nèi)容清零后ByteBuf為==============>"+buf.toString());
System.out.println("7.ByteBuf中的內(nèi)容為================>"+Arrays.toString(buf.array())+"\n");
// 8.再次寫入一段超過容量的內(nèi)容
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("寫入一段內(nèi)容后ByteBuf為===========>"+buf.toString());
System.out.println("8.ByteBuf中的內(nèi)容為===============>"+Arrays.toString(buf.array())+"\n");
}
執(zhí)行結(jié)果如下:
原始ByteBuf為====================>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 10)
1.ByteBuf中的內(nèi)容為===============>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
寫入的bytes為====================>[1, 2, 3, 4, 5]
寫入一段內(nèi)容后ByteBuf為===========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 5, cap: 10)
2.ByteBuf中的內(nèi)容為===============>[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
讀取的bytes為====================>[1, 2]
讀取一段內(nèi)容后ByteBuf為===========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 2, widx: 5, cap: 10)
3.ByteBuf中的內(nèi)容為===============>[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
將讀取的內(nèi)容丟棄后ByteBuf為========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 3, cap: 10)
4.ByteBuf中的內(nèi)容為===============>[3, 4, 5, 4, 5, 0, 0, 0, 0, 0]
將讀寫指針清空后ByteBuf為==========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 10)
5.ByteBuf中的內(nèi)容為===============>[3, 4, 5, 4, 5, 0, 0, 0, 0, 0]
寫入的bytes為====================>[1, 2, 3]
寫入一段內(nèi)容后ByteBuf為===========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 3, cap: 10)
6.ByteBuf中的內(nèi)容為===============>[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
將內(nèi)容清零后ByteBuf為==============>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 3, cap: 10)
7.ByteBuf中的內(nèi)容為================>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
寫入的bytes為====================>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
寫入一段內(nèi)容后ByteBuf為===========>UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 14, cap: 64)
8.ByteBuf中的內(nèi)容為===============>[0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
執(zhí)行過程分析
下面讓我們來仔細(xì)的研究下執(zhí)行的過程,并分析下為什么會(huì)產(chǎn)生這樣的執(zhí)行結(jié)果。
1.初始化一個(gè)大小為10的ByteBuf
剛初始化的ByteBuf對(duì)象,容量為10,讀寫指針都為0,且每個(gè)字節(jié)的值都為0,并且這些字節(jié)都是“可寫”的,我們用紅色來表示。
2.寫入一段內(nèi)容
當(dāng)寫入一段內(nèi)容后(這里寫入的是5個(gè)字節(jié)),寫指針向后移動(dòng)了5個(gè)字節(jié),寫指針的值變成了5,而讀指針沒有發(fā)生變化還是0,但是讀指針和寫指針之間的字節(jié)現(xiàn)在變成了“可讀”的狀態(tài)了,我們用紫色來表示。
3.讀取一段內(nèi)容
接著我們有讀取了2個(gè)字節(jié)的內(nèi)容,這時(shí)讀指針向后移動(dòng)了2個(gè)字節(jié),讀指針的值變成了2,寫指針不變,此時(shí)0和讀指針之間的內(nèi)容變成了“可丟棄”的狀態(tài)了,我們用粉色來表示。
4.將讀取的內(nèi)容丟棄
緊接著,我們將剛剛讀取完的2個(gè)字節(jié)丟棄掉,這時(shí)ByteBuf把讀指針與寫指針之間的內(nèi)容(即 3、4、5 三個(gè)字節(jié))移動(dòng)到了0的位置,并且將讀指針更新為0,寫指針更新為原來寫指針的值減去原來讀指針的值。但是需要注意的是,第4和第5個(gè)字節(jié)的位置上,還保留的原本的內(nèi)容,只是這兩個(gè)字節(jié)由原來的“可讀”變成了現(xiàn)在的“可寫”。
5.將讀寫指針清空
然后,我們執(zhí)行了一個(gè) clear 方法,將讀寫指針同時(shí)都置為0了,此時(shí)所有的字節(jié)都變成“可寫”了,但是需要注意的是,clear方法只是更改的讀寫指針的值,每個(gè)位置上原本的字節(jié)內(nèi)容并沒有發(fā)生改變。
6.再次寫入一段較少的內(nèi)容
然后再次寫入一段內(nèi)容后,讀指針不變,寫指針向后移動(dòng)了具體的字節(jié)數(shù),這里是向后移動(dòng)了三個(gè)字節(jié)。且寫入的這三個(gè)字節(jié)變成了“可讀”狀態(tài)。
7.將ByteBuf中的內(nèi)容清零
清零(setZero)和清空(clear)的方法是兩個(gè)概念完全不同的方法,“清零”是把指定位置上的字節(jié)的值設(shè)置為0,除此之外不改變?nèi)魏蔚闹?#xff0c;所以讀寫指針的值和字節(jié)的“可讀寫”狀態(tài)與上次保持一致,而“清空”則只是將讀寫指針都置為0,并且所有字節(jié)都變成了“可寫”狀態(tài)。
8.寫入一段超過容量的內(nèi)容
最后我們往ByteBuf中寫入超過ByteBuf容量的內(nèi)容,這里是寫入了11個(gè)字節(jié),此時(shí)ByteBuf原本的容量不足以寫入這些內(nèi)容了,所以ByteBuf發(fā)生了擴(kuò)容。其實(shí)只要寫入的字節(jié)數(shù)超過可寫字節(jié)數(shù),就會(huì)發(fā)生擴(kuò)容了。
擴(kuò)容分析
那么擴(kuò)容是怎么擴(kuò)的呢,為什么容量從10擴(kuò)容到64呢?我們從源碼中找答案。
擴(kuò)容肯定發(fā)生在寫入字節(jié)的時(shí)候,讓我們找到 writeBytes(byte[] bytes) 方法,具體如下:
@Override
public ByteBuf writeBytes(byte[] src) {
writeBytes(src, 0, src.length);
return this;
}
@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
// 該方法檢查是否有足夠的可寫空間,是否需要進(jìn)行擴(kuò)容
ensureWritable(length);
setBytes(writerIndex, src, srcIndex, length);
writerIndex += length;
return this;
}
在進(jìn)入 ensureWritable(length) 方法內(nèi)部查看,具體的代碼如下:
@Override
public ByteBuf ensureWritable(int minWritableBytes) {
if (minWritableBytes < 0) {
throw new IllegalArgumentException(String.format(
"minWritableBytes: %d (expected: >= 0)", minWritableBytes));
}
ensureWritable0(minWritableBytes);
return this;
}
final void ensureWritable0(int minWritableBytes) {
// 檢查該ByteBuf對(duì)象的引用計(jì)數(shù)是否為0,保證該對(duì)象在寫入之前是可訪問的
ensureAccessible();
if (minWritableBytes <= writableBytes()) {
return;
}
if (minWritableBytes > maxCapacity - writerIndex) {
throw new IndexOutOfBoundsException(String.format(
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
writerIndex, minWritableBytes, maxCapacity, this));
}
// Normalize the current capacity to the power of 2.
// 計(jì)算新的容量,即為當(dāng)前容量擴(kuò)容至2的冪次方大小
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
// Adjust to the new capacity.
// 設(shè)置擴(kuò)容后的容量
capacity(newCapacity);
}
從上面的代碼中可以很清楚的看出來,計(jì)算新的容量的方法是調(diào)用的 ByteBufAllocator 的 calculateNewCapacity() 方法,繼續(xù)跟進(jìn)去該方法,這里的 ByteBufAllocator 的實(shí)現(xiàn)類是 AbstractByteBufAllocator ,具體的代碼如下:
@Override
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
if (minNewCapacity < 0) {
throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expected: 0+)");
}
if (minNewCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
minNewCapacity, maxCapacity));
}
// 擴(kuò)容的閾值,4兆字節(jié)大小
final int threshold = CALCULATE_THRESHOLD; // 4 MiB page
if (minNewCapacity == threshold) {
return threshold;
}
// If over threshold, do not double but just increase by threshold.
// 如果要擴(kuò)容后新的容量大于擴(kuò)容的閾值,那么擴(kuò)容的方式改為用新的容量加上閾值,
// 否則將新容量改為雙倍大小進(jìn)行擴(kuò)容
if (minNewCapacity > threshold) {
int newCapacity = minNewCapacity / threshold * threshold;
if (newCapacity > maxCapacity - threshold) {
newCapacity = maxCapacity;
} else {
newCapacity += threshold;
}
return newCapacity;
}
// Not over threshold. Double up to 4 MiB, starting from 64.
// 如果要擴(kuò)容后新的容量小于4兆字節(jié),則從64字節(jié)開始擴(kuò)容,每次雙倍擴(kuò)容,
// 直到小于指定的新容量位置
int newCapacity = 64;
while (newCapacity < minNewCapacity) {
newCapacity <<= 1;
}
return Math.min(newCapacity, maxCapacity);
}
到這里就很清楚了,每次擴(kuò)容時(shí),有一個(gè)閾值t(4MB),計(jì)劃擴(kuò)容的大小為c,擴(kuò)容后的值為n。
擴(kuò)容的規(guī)則可以用下面的邏輯表示:
如果c
如果c>t,則n=c/t*t+t
得出的結(jié)論
ByteBuf有讀和寫兩個(gè)指針,用來標(biāo)記“可讀”、“可寫”、“可丟棄”的字節(jié)
調(diào)用write*方法寫入數(shù)據(jù)后,寫指針將會(huì)向后移動(dòng)
調(diào)用read*方法讀取數(shù)據(jù)后,讀指針將會(huì)向后移動(dòng)
寫入數(shù)據(jù)或讀取數(shù)據(jù)時(shí)會(huì)檢查是否有足夠多的空間可以寫入和是否有數(shù)據(jù)可以讀取
寫入數(shù)據(jù)之前,會(huì)進(jìn)行容量檢查,當(dāng)剩余可寫的容量小于需要寫入的容量時(shí),需要執(zhí)行擴(kuò)容操作
擴(kuò)容時(shí)有一個(gè)4MB的閾值,需要擴(kuò)容的容量小于閾值或大于閾值所對(duì)應(yīng)的擴(kuò)容邏輯不同
clear等修改讀寫指針的方法,只會(huì)更改讀寫指針的值,并不會(huì)影響B(tài)yteBuf中已有的內(nèi)容
setZero等修改字節(jié)值的方法,只會(huì)修改對(duì)應(yīng)字節(jié)的值,不會(huì)影響讀寫指針的值以及字節(jié)的可讀寫狀態(tài)
總結(jié)
以上是生活随笔為你收集整理的bytebuf池_图文分析ByteBuf是什么的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python数据类型总结_Python
- 下一篇: python二级模拟选择题集错网_计算机