Netty之ByteBuf

本文详细介绍了Netty中ByteBuf的特性和优势,包括其如何改进传统的ByteBuffer限制,提供了更强大的缓冲区管理功能,如自动扩展、高效读写操作及内存管理等。

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

 

       在使用NIO时需要使用缓冲区,常用的缓冲区就是JDK NIO类库提供的java.nio.Buffer,而主要使用的是ByteBuffer。但是ByteBuffer也有局限性:
       1.ByteBuffer长度固定,一旦分配完成,它的容量就不能动态扩展和收缩,当需要编码的POJO对象大于ByteBuffer的容量时,会发送索引越界异常;
       2.ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()和rewind()等,使用者必须小心谨慎地处理这些API,否则很容易导致程序处理失败;
       3.ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程。

       为弥补这些不足,Netty提供了自己的ByteBuffer实现——ByteBuf。ByteBuf依然是个Byte数组的缓冲区,它的基本功能应该与JDK的ByteBuffer一致,提供以下几类基本功能:
       1. 7种Java基础类型、byte数组、ByteBuffer等的读写;
       2. 缓冲区自身的copy和slice等;
       3. 设置网络字节序;
       4. 构造缓冲区实例;
       5. 操作位置指针等方法;

 

 

 

       ByteBuf通过2个位置指针来协助缓冲区的读写操作,读操作使用readerIndex,写操作使用writerIndex
       readerIndex和writerIndex的取值一开始都是0,随着数据的写入writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex。在读取之后,0~readerIndex的就被视为discard的,调用discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffer的compact方法。ReaderIndex和writeIndex之间的数据是可读取的,等价于ByteBuffer的positon和limit之间的数据。writerIndex和capacity之间的空间是可写的,等价于ByteBuffer的limit和capacity之间的可用空间。
       由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不再需要调整位置指针,这极大地简化了缓冲区的读写操作,避免了由于遗漏或者不熟悉flip()操作导致的功能异常.

 

   

    

    

     

        

 

       需要注意的是,调用discardReadBytes会发生字节数组的内存复制,所以频繁调用将会导致性能下降。

 

 

 

 

 

 

       当对缓冲区进行读操作时,由于某些原因,可能需要对之前的操作进行回滚。读操作并不会改变缓冲区的内容,回滚操作主要就是重新设置索引信息。
       对于JDK的ByteBuffer,调用mark操作会将当前的位置指针备份到mark变量中,当调用reset操作之后,重新将指针的当前位置恢复为备份在mark中的值。
       Netty的ByteBuf也有类似的接口。因为ByteBuf有读索引和写索引,因此,它总共有4个相关的方法:markReaderIndex、resetReaderIndex、markWriterIndex、resetWriterIndex。


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

 

 

 

ByteBuf的主要类继承关系:

      

 

 

从内存分配的角度看,ByteBuf可以分2类:
       1. 堆内存(HeapByteBuf)字节缓冲区:特点是内存的分配和回收速度快,可以被JVM自动回收;缺点就是如果进行Socket的I/O读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内存Channel中,性能会有一定程度的下降;
       2. 直接内存(DirectByteBuf)字节缓冲区:非堆内存,它在堆外进行内存分配,相对于堆内存,它的分配和回收速度会慢一些,但是将它写入或者从Socket Channel中读取时,由于少了一次内存复制,速度比堆内存快。
       最佳实践:在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的编解码模块使用HeapByteBuf。

 

 

从内存回收角度看,ByteBuf分2类:

       基于对象池的ByteBuf和普通ByteBuf。

       两者的主要区别就是基于对象池的ByteBuf可以重用ByteBuf对象,它自己维护一个内存池,可以循环利用创建的ByteBuf,提高内存使用效率,降低由于高负载导致的频繁GC。测试表明使用内存池后的Netty在高负载、大并发的冲击下内存和GC更加平稳。

### NettyByteBuf 的用法及常见问题解析 #### 1. ByteBuf 的基本概念 ByteBufNetty 提供的一种高效缓冲区管理工具,用于替代 JDK 自带的 ByteBuffer。它具有更灵活的功能设计和更高的性能表现[^1]。 #### 2. 创建 ByteBuf 实例 以下是创建 ByteBuf 的简单示例代码: ```java public class NettyByteBufExample { public static void main(String[] args) { // 默认分配器创建 ByteBuf 对象 ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(); System.out.println(buf); } } ``` 上述代码展示了如何通过 `ByteBufAllocator` 来获取默认的 ByteBuf 实例[^2]。 #### 3. ByteBuf 的存储结构 ByteBuf 的内部存储结构分为三个主要部分:读索引 (`readerIndex`)、写索引 (`writerIndex`) 和容量 (`capacity`)。这些字段共同决定了数据的操作范围和边界。 #### 4. 控制池化功能 Netty 支持通过环境变量配置是否启用内存池机制。如果希望禁用内存池,则可以通过设置如下参数实现: ```properties -Dio.netty.allocator.type=unpooled ``` 反之,若要启用内存池,则将值设为 `pooled` 即可。 #### 5. 高度可扩展性 ByteBuf 接口允许开发者根据实际需求对其进行扩展或自定义实现。例如,在某些特殊场景下可能需要增加额外的方法或属性支持,此时可通过继承现有类或者重新实现接口完成定制开发工作[^4]。 #### 6. 内存泄漏检测 为了防止因不当操作而导致资源未释放而引发的潜在风险,Netty 设计了一套完善的内存泄露监控体系。当发现异常情况时会自动记录相关信息以便排查问题所在位置[^3]。 --- ### 示例代码展示 以下是一个简单的 ByteBuf 使用案例: ```java import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; public class SimpleByteBufUsage { public static void main(String[] args) { // 创建一个非池化的 ByteBuf ByteBuf buffer = Unpooled.buffer(10); // 向 Buffer 写入字节数据 String message = "Hello"; byte[] bytes = message.getBytes(); buffer.writeBytes(bytes); // 输出当前状态信息 System.out.println("Reader Index: " + buffer.readerIndex()); System.out.println("Writer Index: " + buffer.writerIndex()); // 移动 Reader Index 并读取第一个字符 char firstChar = (char) buffer.readByte(); System.out.println("First Character: " + firstChar); // 清理资源 buffer.release(); } } ``` 此程序演示了如何向 ByteBuf 写入字符串并从中提取单个字符的过程。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值