ByteBuf用法详解

ByteBuf from Netty

ByteBuf from Netty比ByteBuffer from java nio更强大,比如可以进行池化,不需要flip切换读写模式。
ByteBuf内部结构如图所示:
ByteBuf本质上是一个字节数组,分为了4个部分。

在这里插入图片描述
readerIndex是读指针,每读取一个字节,readerIndex就会+1。一旦readerIndex = writerIndex,不可再读了。
writerIndex是写指针,每写入一个字节,writerIndex就会+1.一旦writerIndex = readerIndex,不可再写了。
capacity表示ByteBuf容量,它的值 = 废弃的字节数 + 可读的字节数 + 可写的字节数。
读取之后,0~readerIndex就被视为废弃的,调用discardReadBytes方法,可以释放这部分空间。
maxCapacity是ByteBuf可以扩容的最大容量。扩容超过maxCapacity就会报错。

示例1

@Test
public void test1(){
    ByteBuf b = ByteBufAllocator.DEFAULT.buffer(1,10);
    System.out.println("可读字节数:" + b.readableBytes() + ",可写字节数:" + b.writableBytes());
    System.out.println("分配了初始容量是9,最大容量10个字节的缓冲区" + b);
    b.writeBytes(new byte[]{1,2});
    System.out.println("写入了2个字节" + b);
    System.out.println("可读字节数:" + b.readableBytes() + ",可写字节数:" + b.writableBytes());
    getByteBuf(b);
    System.out.println("可读字节数:" + b.readableBytes() + ",可写字节数:" + b.writableBytes());
    System.out.println(b);
    readByteBuf(b);
    System.out.println("可读字节数:" + b.readableBytes() + ",可写字节数:" + b.writableBytes());
    System.out.println(b);
    getByteBuf(b);
    System.out.println("可读字节数:" + b.readableBytes() + ",可写字节数:" + b.writableBytes());
    System.out.println(b);
}

private void readByteBuf(ByteBuf buffer){
    System.out.println("开始读取,改变了buffer内部指针,buffer内容如下:");
    while(buffer.isReadable()){
        System.out.print(buffer.readByte() + ",");
    }
    System.out.println();
}

/**
 * 读取字节,不改变指针
 * @param buffer
 */
private void getByteBuf(ByteBuf buffer){
    System.out.println("开始读取,不改变buffer内部指针,buffer内容如下:");
    for (int i = 0; i < buffer.readableBytes(); i++) {
        System.out.print(buffer.getByte(i) + ",");
    }
    System.out.println();
}

运行结果:

可读字节数:0,可写字节数:1
分配了初始容量是9,最大容量10个字节的缓冲区PooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 1/10)
写入了2个字节PooledUnsafeDirectByteBuf(ridx: 0, widx: 2, cap: 10/10)
可读字节数:2,可写字节数:8
开始读取,不改变buffer内部指针,buffer内容如下:
1,2,
可读字节数:2,可写字节数:8
PooledUnsafeDirectByteBuf(ridx: 0, widx: 2, cap: 10/10)
开始读取,改变了buffer内部指针,buffer内容如下:
1,2,
可读字节数:0,可写字节数:8
PooledUnsafeDirectByteBuf(ridx: 2, widx: 2, cap: 10/10)
开始读取,不改变buffer内部指针,buffer内容如下:

可读字节数:0,可写字节数:8
PooledUnsafeDirectByteBuf(ridx: 2, widx: 2, cap: 10/10)

toString()

上例中打印ByteBuf的toString()方法中显示的ridx、widx、cap源码如下所示:
abstract class AbstractByteBuf extends ByteBuf
其中capacity()表示ByteBuf容量,它的值 = 废弃的字节数 + 可读的字节数 + 可写的字节数。

@Override
public String toString() {
    if (refCnt() == 0) {
        return StringUtil.simpleClassName(this) + "(freed)";
    }

    StringBuilder buf = new StringBuilder()
        .append(StringUtil.simpleClassName(this))
        .append("(ridx: ").append(readerIndex)
        .append(", widx: ").append(writerIndex)
        .append(", cap: ").append(capacity());
    if (maxCapacity != Integer.MAX_VALUE) {
        buf.append('/').append(maxCapacity);
    }

    ByteBuf unwrapped = unwrap();
    if (unwrapped != null) {
        buf.append(", unwrapped: ").append(unwrapped);
    }
    buf.append(')');
    return buf.toString();
}

ByteBuf内存回收

Netty的ByteBuf支持缓存池技术,目的是提高频繁创建的ByteBuf的利用率,避免系统内存的浪费。
池子里ByteBuf的回收通过引用计数的方式。每次调用retain()方法,引用+1,每次调用release()方法,引用计数-1。刚创建的ByteBuf计数为1。
如果引用计数=0,
(1)如果是Pooled池化的ByteBuf,继续放入可以重新分配的池子里。
(2)如果是Unpooled未池化的,回收分两种情况:如果是堆结构,JVM垃圾回收。如果是Direct类型,调用本地方法(unsafe.freeMemory)释放内存。

@Test
public void test2(){
    ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
    System.out.println(buffer.refCnt());  //1
    buffer.retain();
    System.out.println(buffer.refCnt());  //2
    buffer.retain();
    System.out.println(buffer.refCnt());  //3
    buffer.release();
    System.out.println(buffer.refCnt());  //2
    buffer.retain();
    System.out.println(buffer.refCnt());   //3
}

Allocator分配器

ByteBufAllocator有2个重要的实现类:PoolByteBufAllocator(池化的ByteBuf分配器)、UnpooledByteBufAllocator(非池化的ByteBuf分配器)。
在这里插入图片描述
PoolByteBufAllocator采用池化技术,能够提高ByteBuf的重复利用率。
UnpooledByteBufAllocator每次用完,通过GC回收。
默认是:
在这里插入图片描述

/**
 * 使用分配器分配ByteBuf的方法
 */
/**
 * 分配初始容量1,最大10的缓冲区
 */
ByteBufAllocator.DEFAULT.buffer(1,10);
/**
 * 分配默认256,最大Integer.MAX_VALUE的缓冲区
 */
ByteBufAllocator.DEFAULT.buffer();
/**
 * 非池化分配器,分配在堆内存的缓冲区
 */
ByteBufAllocator.DEFAULT.heapBuffer();
/**
 * 直接内存缓冲区,不属于堆内存,归属于OS内存
 */
ByteBufAllocator.DEFAULT.directBuffer();

Unpooled类

Unpooled是Netty提供的用来操作缓冲区的工具类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值