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可以回收空间,但是这操作极有可能导致内存复制,因为需要把读索引后面的字节复制到缓冲区的开始位置。