第 5 章 ByteBuf

5.1 ByteBuf 的 API

    Netty 提供了两个组件:abstract class  ByteBuf 和 interface ByteBufHolder

    有以下一些优点

  1.     它可以被用户自定义的缓冲区类型扩展
  2.     通过内置的复合缓冲区类型实现了透明的零拷贝
  3.     容量按需增长
  4.     在和写这两种模式之间的切换不需要调用 ByteBuffer 的 flip()方法
  5.     读和写使用了不同的索引
  6.     支持方法链式调用
  7.     支持引用计数
  8.     支持池化

5.2 ByteBuf 类详解   

5.2.1 工作方式

    ByteBuf 维护了两个索引:一个用户读取(readIndex),一个用于写入(writeIndex)。当你从 ByteBuf读取时,它的 readIndex 将会被递增已经读取的字节数,写入的时候类似。需要注意的是:0 <= readIndex <= writeIndex <= capacity <= Integer.MAX_VALUE。readIndex 和 writeIndex 把 ByteBuf 分成了3部分


    维护两个索引,好处也是显而易见的:可以清晰地判断已读字节数,剩余可读字节数。 

    可丢弃的字节可以通过 discardReadBytes() 方法来回收内存空间。调用之后,可读字节需要复制到 ByteBuf 的开始索引,readIndex 会变为0,writeIndex 也做相应的改变,如下图


    “回收内存”空间的方法还有一种就是通过 clear 方法,这种方法相比 discardReadBytes 方法来说轻量了很多,因为它只是把 readIndex 和 writeIndex 设置为 0 了,并没有牵扯到数据的复制  。  

    在写入数据的时候,先判断存储空间是否足够,类似于 buffer.writableBytes() >= xxx;

5.2.2 ByteBuf 的类型

    堆缓冲区(heapByteBuf)

扫描二维码关注公众号,回复: 1743868 查看本文章

    这是最常用的 ByteBuf 模式,它有个别名——支撑数组(backing array),也就是把数据存储在 JVM 的堆空间中(实际就是把数据存储在一个数组里面)。

    优点:数据存储在堆内存,jvm 分配和释放的内存的效率比较高,数据访问比较方便

    直接缓冲区(directByteBuf)

    缓存数据的内存空间在堆之外的某块内存区域。

    主要优点

    对于利用网络传输数据时,使用直接缓冲区效率更高。因为 JVM 在利用 Socket 进行网络传输本地数据时,如果本地数据存储在堆内存上,那么 JVM 内部会把堆中的数据先复制到堆之外的缓冲区,然后进行传输。

    主要缺点

    相对于堆缓冲区,它们的分配和释放都比较昂贵。所以通过内存池来解决这个问题。

    访问直接缓冲数据比较麻烦,不支持数据的直接访问。若想访问数据,首先你得先把数据从直接缓冲区复制到一个字节数组(堆内存),然后才能读取。所以如果提前知道数据会被经常访问,推荐使用堆缓冲区

    复合缓冲区(compositeByteBuf)

    复合缓冲区可以理解为多个 ByteBuf 的一个聚合视图,你可以该缓冲区中添加或者删除 ByteBuf(直接缓冲区或者堆缓冲区),操作形式上类似于 ChannelPipeLine 和 ChannelHandler之间的关系。类似于下图


    这种方式的优势在于便于统一处理多个缓冲区

5.4 ByteBufAllocator 接口

    为了降低分配和释放内存的开销,Netty 通过 接口 ByteBufAllocator 实现了(ByteBuf 的)池化。它可以分配任意类型的 ByteBuf 实例。

    ByteBufAllocator 可以通过 Channel 的 alloc() 方法获取到,或者 ChannelHandlerContext 的 alloc() 方法获取到。

    Netty 提供了两种 ByteBufAllocator 的实现:PooledByteBufAllocator(池化) 和 UnPooledByteBufAllocator(非池化)。在 Netty 4.1.x 版本后,Netty 默认使用了 PooledByteBufAllocator 这种实现方式。

5.5 引用计数

    当某个对象的资源(resource)被对象1、对象2引用的时候,我们称 resource 的引用计数为 2。当对象1、对象2 不在使用 resource 的时候,我们称 resource 的引用计数为 0 ,这时候该资源就可以被销毁,回收 resource 所占的存储空间了。所以,引用计数实际上就是一种通过在某个对象的资源不在被其他对象所引用的时候释放该对象所持有的资源来优化内存的一种计数。

    Netty 在 4 版中对 ByteBuf 和 ByteBufHolder 引入了引用计数的技术。引用计数对于池化的实现来说至关重要,它降低了内存分配的开销。

总结

    本章节主要学习了

    byteBuf 通过两个读、写索引来维护数据,使得操作更加灵活

    byteBuf的三种类型:堆缓冲区(基于数据)、直接缓冲区(基于非堆内存)、复合缓冲区(相当于多个 ByteBuf 的聚合视图,使得操作多个不同 ByteBuf 更加灵活)

    池化以及引用计数

    读、写、获取、复制API(操作相关

    下一章 ChannelHander 的专辑


    



猜你喜欢

转载自blog.csdn.net/yhs1296997148/article/details/80458380