码迷,mamicode.com
首页 > 其他好文 > 详细

ByteBuf

时间:2016-05-06 15:18:50      阅读:313      评论:0      收藏:0      [点我收藏+]

标签:

       在使用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更加平稳。

ByteBuf

标签:

原文地址:http://blog.csdn.net/zero__007/article/details/51326657

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!