这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
前言
在了解了 ByteBuffer 的原理之后,再来理解Netty 的 ByteBuf 就比较简单了。
ByteBuf 是 Netty 框架封装的数据缓冲区,区别于 position、limit、flip等属性和操作来控制 ByteBuffer 的读写,ByteBuf 通过两个位置指针来协助缓冲区的读写操作,分别是readIndex和writeIndex。
readIndex、writeIndex和capacity变量存在以下关系:
1 | 复制代码0 <= readIndex <= writeIndex <= capacity |
实现原理
初始化 ByteBuffer 时,readIndex 和 writeIndex 取值一开始都是0。如下图所示:
当执行写入数据之后,writeIndex会增加,如下图所示:
当执行读入数据之后则会使readIndex增加,但不会超过writeIndex,如下图:
在读取之后,索引 0 到 readIndex位置的区域被视为废弃字节(discard)。可以调用discardReadBytes方法,来释放这部分空间,其作用类似于 ByteBuffer的compact()方法,移除无用的数据,实现缓冲区的重复利用。如下图,展示了执行discardReadBytes后的情况,相当于可写的空间变大了。
ByteBuf 的使用案例
为了更好的理解ByteBuf,编写了以下示例:
1 | java复制代码public class ByteBufDemo { |
输出结果:
1 | java复制代码------------初始时缓冲区------------ |
对比ByteBuffer和ByteBuf两个示例可以看出,Netty 提供了更加方便地创建ByteBuf的工具(unpooled),同时,也不必再执行flip()方法来切换读写模式。对比而言,ByteBuf更加易于使用。
ByteBuf 的3种使用模式
ByteBuf 共有三种使用模式:堆缓冲区模式(Heap Buffer)、直接缓冲区模式(Direct Buffer)和 复合缓冲区模式(Composite Buffer)。
堆缓冲模式
堆缓冲区模式又称为
支撑数组,其数据是存放在JVM的堆空间,通过将数据存储在数组中实现。
优点:数据存储在JVM堆中可以快速的创建和快速释放,并且提供了数据快速访问的方法;
缺点:每次数据与 I/O 进行传输时,都需要将数据复制到直接缓冲区。
以下是堆缓冲区的代码示例:
1 | java复制代码public class ByteBufHeapBufferDemo { |
输出结果:
1 | java复制代码array:[B@5b37e0d2 |
直接缓冲区模式
直接缓冲区属于堆外分配的直接内存,不会占用堆得空间。
优点:使用 socket 传输数据时性能很好,避免了数据从 JVM 堆内存复制到直接缓冲区的过程,提高了性能。
缺点:相对于堆缓冲区而言,直接缓冲区分配内存空间和释放更为昂贵。
对于涉及大量的 I/O 数据的读写,建议使用直接缓冲区。而对于用于后端业务消息编解码模块,建议使用堆缓冲区。
以下是直接缓冲区代码示例:
1 | java复制代码public class ByteBufDirectBufferDemo { |
输出结果:
1 | java复制代码array:[B@6d5380c2 |
复合缓冲区模式
复合缓冲区是 Netty 特有的缓冲区。本质上类似于提供一个或多个
ByteBuf的组合视图,可以根据需要添加和删除不同类型的ByteBuf。
优点:提供了一种访问方式让使用者自由地组合多个ByteBuf,避免了复制和分配新的缓冲区。
缺点:不支持访问其支撑数组。因此如果要访问,需要先将内容复制到堆内存中,再进行访问。
以下示例是复合缓冲区将堆缓冲区和直接缓冲区组合在一起,没有进行任何复制过程,仅仅创建了一个视图而已。
1 | java复制代码public class ByteBufCompositeBufferDemo { |
输出结果:
1 | java复制代码array:[B@4d76f3f8 |
CompositeByteBuf是一个虚拟的缓冲区,其用途是将多个缓冲区显示为单个合并缓冲区,类似数据库中的视图。
总结
通过以上对于ByteBuf的介绍,相信小伙伴们对于ByteBuf的原理也有了一定的了解。下一节我们继续深入Netty的源码。
结尾
我是一个正在被打击还在努力前进的码农。如果文章对你有帮助,记得点赞、关注哟,谢谢!
本文转载自: 掘金