Netty学习之十二 —— ByteBuf 和相关辅助类

本文对比分析了ByteBuf与ByteBuffer在数据传输中的优劣。详细介绍了ByteBuffer的局限性,包括固定长度、复杂的读写操作及有限的API支持。重点阐述了ByteBuf的改进,如动态扩展能力、更简洁的读写操作及更高的缓冲区重用率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当我们进行数据传输的时候,往往需要使用到缓冲区,常用的缓冲区是 JDK NIO 类库提供的 java.nio.Buffer。

ByteBuffer的缺点:

1. ByteBuffer 长度固定,一旦分配完成,它的容量不能动态扩展和收缩,当需要编码的 POJO 对象大于 ByteBuffer 的容量时,会发生索引越界异常;

2. ByteBuffer 只有一个标识位置的指针 position,读写的时候需要手工调用 flip() 和 rewind() 等,使用者必须小心谨慎地处理这些 API,否则很容易导致程序处理失败;

3. ByteBuffer 的 API 功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。

1、ByteBuf 功能说明

1.JDK ByteBuffer 由于只有一个位置指针用于处理读写操作, 因此每次读写的时候都需要额外调用 flip() 和 clear() 等方法否则功能将出错。

ByteBuffer buffer = ByteBuffer.allocate(88);
String value = "Netty 权威指南";
buffer.put(value.getBytes());
buffer.flip();
byte[] vArray = new byte[buffer.remaining()];
buffer.get(vArray);
String decodeValue = new String(vArray)

下面看一下调用 flip() 操作前后的对比。

如果不做 flip 操作,读取到的将是position 到 capacity 之间的错误内容。

当执行 flip() 操作之后,它的 limit 被设置为 position, position 设置为 0,capacity不变。由于读取的内容是从position 到 limit 之间因此它能够正确地读取到之前写入缓冲区的内容。

2、ByteBuf 通过两个位置指针来协助缓冲区的读写操作,读操作使用 readerIndex,写操作使用 writerIndex。

readerIndex 和 writerIndex 的取值一开始都是0,随着数据的写入 writerIndex 会增加,读取数据会使 readerIndex 增加,但是它不会超过 writerIndex。在读取之后,0 ~ readerIndex 被视为 discard (已读)的,调用 discardReadBytes 方法,可以释放这部分空间,它的作用类似于 ByteBuffer 的compact方法。ReaderIndex 和 writerIndex 之间的数据是可读取的,等价于 ByteBuffer position 和 limit 之间的数据。writerIndex 和capacity 之间的空间是可写的,等价于 ByteBuffer limit 和 capacity 之间的可用空间。

由于写操作不修改 readerIndex指针,读操作不修改 writerIndex 指针,因此读写之间不再需要调整指针位置,这极大地简化了缓冲区的读写操作,避免了由于遗漏或者不熟悉 flip 操作导致的功能异常。

3、ByteBuf 如何实现动态扩展? 

通常情况下,ByteBuffer 进行put 操作的时候,如果缓冲区剩余可写空间不够,就会发生 BufferOverflowException异常。为了避免发生这个问题,通常在进行 put 操作的时候会对剩余可用空间进行校验。如果剩余空间不足,需要重新创建一个 新的 ByteBuffer, 并将之前的 ByteBuffer 复制到新创建的 ByteBuffer 中,最后释放老的ByteBuffer。

ByteBuf 对 write 操作进行了封装,由 ByteBuf 的 write 操作负责进行剩余可用空间的校验,如果可用缓冲区不足,ByteBuf 会自动进行扩展。通过源码分析,我们发现当进行 write 操作时,会对需要 write 的字节进行校验。如果可写的字节数小于 需要写入的字节数,并且需要写入的字节数小于可写的最大字节数,就对缓冲区进行动态扩展。

2、ByteBuf 的 API介绍

1. 顺序读操作

ByteBuf 的 read操作类似于 ByteBuffer 的 get 操作,

2. 顺序写操作

ByteBuf 的 write 操作类似于 ByteBuffer 的 put 操作。

3. readerIndex 和 writerIndex

这两个指针变量用于支持顺序读取和写入操作:readerIndex 用于标识读取索引,writerIndex 用于标识写入索引。

4. Discardable bytes

相比于其他的 Java 对象,缓冲区的分配和释放是个耗时的操作,因此,我们需要尽量重用它们。由于缓冲区的动态扩张需要进行字节数组的复制,它是个耗时的操作,因此,为了最大程度地提升性能,往往需要尽最大努力提升缓冲区的重用率。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值