1

NIO的Buffer之前介绍过了,这边来看看Netty的ByteBuf。

ByteBuf

ByteBuf维护了两个不同的索引:一个用于读取,一个用于写入,分别对应readerIndex和writerIndex。每次读的时候,readerIndex加1,写的时候writerIndex加1。在使用时,即可以使用堆缓冲区,也可以使用直接缓冲区,还可以使用复合缓冲区。
ByteBuf的内部分段如下,小于readerIndex的说明已经读取完了,为可丢弃字节,大于readerIndex小于writerIndex的为可读字节,大于writerIndex小于capacity的为可写字节。
image.png
为了下面例子方便,先贴一个初始化代码,定义了一个初始容量为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;
    }

write和read

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

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:

get 和 set

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方法,并不会对索引有影响。

discardReadBytes

用于丢弃字节并回收空间。

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


大军
847 声望183 粉丝

学而不思则罔,思而不学则殆