NIO的Buffer之前介绍过了,这边来看看Netty的ByteBuf。
ByteBuf维护了两个不同的索引:一个用于读取,一个用于写入,分别对应readerIndex和writerIndex。每次读的时候,readerIndex加1,写的时候writerIndex加1。在使用时,即可以使用堆缓冲区,也可以使用直接缓冲区,还可以使用复合缓冲区。
ByteBuf的内部分段如下,小于readerIndex的说明已经读取完了,为可丢弃字节,大于readerIndex小于writerIndex的为可读字节,大于writerIndex小于capacity的为可写字节。
为了下面例子方便,先贴一个初始化代码,定义了一个初始容量为4,最大容量为8的ByteBuf。如果扩容的话,至少64,8太小会报错,这边就不进行扩容操作。
public static ByteBuf init() { ByteBuf byteBuf = Unpooled.buffer(4, 8); System.out.println("----------init----------"); System.out.println(byteBuf.toString()); System.out.println("init:" + byteBuf.toString(CharsetUtil.UTF_8)); return byteBuf; }
public static void main(String[] args) { ByteBuf byteBuf = init(); writeAndRead(byteBuf); } public static void writeAndRead(ByteBuf byteBuf) { System.out.println("----------write----------"); byteBuf.writeBytes("ab".getBytes()); System.out.println(byteBuf.toString()); System.out.println("write:" + byteBuf.toString(CharsetUtil.UTF_8)); byteBuf.writeBytes("cd".getBytes()); System.out.println(byteBuf.toString()); System.out.println("write:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("----------read----------"); byteBuf.readByte(); System.out.println(byteBuf.toString()); System.out.println("read:" + byteBuf.toString(CharsetUtil.UTF_8)); }
运行结果如下:
----------init---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 4/8) init: ----------write---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) write:ab UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 4, cap: 4/8) write:abcd ----------read---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 1, widx: 4, cap: 4/8) read:bcd
可以看出,初始化的时候,读和写的索引都是0,当写入ab的时候,写的索引为2,写入cd的时候,写的索引为4。读取第一个字节的时候,读的索引加1。
isWritable和isReadable是判断是否有字节允许可写可读。
public static void main(String[] args) { ByteBuf byteBuf = init(); writeAndRead2(byteBuf); } public static void writeAndRead2(ByteBuf byteBuf) { while (byteBuf.isWritable()) { byteBuf.writeBytes("aa".getBytes()); System.out.println(byteBuf.toString()); System.out.println("write:" + byteBuf.toString(CharsetUtil.UTF_8)); } while ((byteBuf.isReadable())){ byteBuf.readByte(); System.out.println(byteBuf.toString()); System.out.println("read:" + byteBuf.toString(CharsetUtil.UTF_8)); } }
运行结果如下:
----------init---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 4/8) init: UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) write:aa UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 4, cap: 4/8) write:aaaa UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 1, widx: 4, cap: 4/8) read:aaa UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 2, widx: 4, cap: 4/8) read:aa UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 3, widx: 4, cap: 4/8) read:a UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 4, widx: 4, cap: 4/8) read:
public static void main(String[] args) { ByteBuf byteBuf = init(); getAndset(byteBuf); } public static void getAndset(ByteBuf byteBuf) { System.out.println("----------write----------"); byteBuf.writeBytes("ab".getBytes()); System.out.println(byteBuf.toString()); System.out.println("set:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("----------set----------"); byteBuf.setByte(0, (byte) 'A'); System.out.println(byteBuf.toString()); System.out.println("set:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("----------get----------"); byte b = byteBuf.getByte(0); System.out.println((char) b); System.out.println(byteBuf.toString()); System.out.println("get:" + byteBuf.toString(CharsetUtil.UTF_8)); }
运行结果如下:
----------init---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 4/8) init: ----------write---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) set:ab ----------set---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) set:Ab ----------get---------- A UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) get:Ab
可以看出,get和set方法,并不会对索引有影响。
用于丢弃字节并回收空间。
public static void main(String[] args) { ByteBuf byteBuf = init(); discardReadBytes(byteBuf); } public static void discardReadBytes(ByteBuf byteBuf) { System.out.println("----------write----------"); byteBuf.writeBytes("ab".getBytes()); System.out.println(byteBuf.toString()); System.out.println("----------read----------"); byteBuf.readByte(); System.out.println(byteBuf.toString()); System.out.println("read:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("----------discardReadBytes----------"); byteBuf.discardReadBytes(); System.out.println(byteBuf.toString()); System.out.println("discardReadBytes:" + byteBuf.toString(CharsetUtil.UTF_8)); }
运行结果如下:
----------init---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 4/8) init: ----------write---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 2, cap: 4/8) ----------read---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 1, widx: 2, cap: 4/8) read:b ----------discardReadBytes---------- UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 1, cap: 4/8) discardReadBytes:b
在调用discardReadBytes方法前,读的索引是1,写的索引是2,可丢弃的字节就是0-1之间,调用discardReadBytes方法后,读的索引是0,写的索引是1。
虽然discardReadBytes可以回收空间,但是这操作极有可能导致内存复制,因为需要把读索引后面的字节复制到缓冲区的开始位置。