ByteBuf 详解

ByteBuf 详解author 鲁伟林记录 Netty 实战 中各章节学习过程 写下一些自己的思考和总结 帮助使用 Netty 框架的开发技术人员们 能够有所得 避免踩坑 本博客目录结构将严格按照书本 Netty 实战 省略与 Netty 无关的内容 可能出现跳小章节 本博客中涉及的完整代码 GitHub 地址 https github com thinkingfioa netty learning tre

1. 堆缓冲区模式(Heap Buffer)

堆缓冲区模式又称为:支撑数组(backing array)。将数据存放在JVM的堆空间,通过将数据存储在数组中实现

Direct Buffer属于堆外分配的直接内存,不会占用堆的容量。适用于套接字传输过程,避免了数据从内部缓冲区拷贝到直接缓冲区的过程,性能较好

Composite Buffer是Netty特有的缓冲区。本质上类似于提供一个或多个ByteBuf的组合视图,可以根据需要添加和删除不同类型的ByteBuf。

代码:

  1. discardReadBytes()效果 —– 将可读字节区域(CONTENT)[readerIndex, writerIndex)往前移动readerIndex位,同时修改读索引和写索引。

  2. discardReadBytes()方法会移动可读字节区域内容(CONTENT)。如果频繁调用,会有多次数据复制开销,对性能有一定的影响

2. readerIndex(int) —– 设置readerIndex为固定的值

3. writerIndex(int) —– 设置writerIndex为固定的值

4. clear() —– 效果是: readerIndex=0, writerIndex(0)。不会清除内存

5. 调用clear()比调用discardReadBytes()轻量的多。仅仅重置readerIndex和writerIndex的值,不会拷贝任何内存,开销较小。

1. 最简单的方法 —– indexOf()

2. 利用ByteProcessor作为参数来查找某个指定的值。

代码:

1. duplicate() 

2. slice()

3. slice(int, int)

4. Unpooled.unmodifiableBuffer(…)

5. Unpooled.wrappedBuffer(…)

6. order(ByteOrder)

7. readSlice(int)

理解

1. 上面的6中方法,都会返回一个新的ByteBuf实例,具有自己的读索引和写索引。但是,其内部存储是与原对象是共享的。这就是视图的概念

2. 请注意:如果你修改了这个新的ByteBuf实例的具体内容,那么对应的源实例也会被修改,因为其内部存储是共享的

3. 如果需要拷贝现有缓冲区的真实副本,请使用copy()或copy(int, int)方法。

4. 使用派生缓冲区,避免了复制内存的开销,有效提高程序的性能

代码:

1. get()和set()操作 —– 从给定的索引开始,并且保持索引不变

2. read()和write()操作 —– 从给定的索引开始,并且根据已经访问过的字节数对索引进行访问

3. 下图给出get()操作API,对于set()操作、read()操作和write操作可参考书籍或API

5.3.10 更多的操作

1. hasArray() —– 如果ByteBuf由一个字节数组支撑,则返回true。通俗的讲:ByteBuf是堆缓冲区模式,则代表其内部存储是由字节数组支撑的。如果还没理解,可参考5.2.2章节

2. array() —– 如果ByteBuf是由一个字节数组支撑泽返回数组,否则抛出UnsupportedOperationException异常。也就是,ByteBuf是堆缓冲区模式

1. ByteBufHolder是ByteBuf的容器,可以通过子类实现ByteBufHolder接口,根据自身需要添加自己需要的数据字段。可以用于自定义缓冲区类型扩展字段。

2. Netty提供了一个默认的实现DefaultByteBufHolder。

代码

public class CustomByteBufHolder extends DefaultByteBufHolder{

    private String protocolName;

1. ctx.channel().alloc().buffer() —– 本质就是: ByteBufAllocator.DEFAULT

2. ByteBufAllocator.DEFAULT.buffer() —– 返回一个基于堆或者直接内存存储的Bytebuf。默认是堆内存

3. ByteBufAllocator.DEFAULT —– 有两种类型: UnpooledByteBufAllocator.DEFAULT(非池化)和PooledByteBufAllocator.DEFAULT(池化)。对于Java程序,默认使用PooledByteBufAllocator(池化)。对于安卓,默认使用UnpooledByteBufAllocator(非池化)

4. 可以通过BootStrap中的Config为每个Channel提供独立的ByteBufAllocator实例

解释:

1. 上图中的buffer()方法,返回一个基于堆或者直接内存存储的Bytebuf —– 缺省是堆内存。源码: AbstractByteBufAllocator() { this(false); }

2. ByteBufAllocator.DEFAULT —– 可能是池化,也可能是非池化。默认是池化(PooledByteBufAllocator.DEFAULT)

注意:

1. 上图的buffer()方法,返回一个未池化的基于堆内存存储的ByteBuf

2. wrappedBuffer() —– 创建一个视图,返回一个包装了给定数据的ByteBuf。非常实用

创建ByteBuf代码:

1. hexdump() —– 以十六进制的表示形式打印ByteBuf的内容。非常有价值 

2. equals() —– 判断两个ByteBuf实例的相等性

1. 谁负责释放: 一般来说,是由最后访问(引用计数)对象的那一方来负责将它释放

2. buffer.release() —– 引用计数减1 

3. buffer.retain() —– 引用计数加1

4. buffer.refCnt() —– 返回当前对象引用计数值

5. buffer.touch() —– 记录当前对象的访问位置,主要用于调试。

6. 引用计数并非仅对于直接缓冲区(direct Buffer)。ByteBuf的三种模式: 堆缓冲区(heap Buffer)、直接缓冲区(dirrect Buffer)和复合缓冲区(Composite Buffer)都使用了引用计数,某些时候需要程序员手动维护引用数值

代码:

2. ByteBuf的三种模式: 堆缓冲区(heap Buffer)、直接缓冲区(dirrect Buffer)和复合缓冲区(Composite Buffer)都使用了引用计数,某些时候需要程序员手动维护引用数值。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/219880.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午9:41
下一篇 2026年3月17日 下午9:41


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号