Netty ByteBuf 使用详解

文章目录

  • 1.概述
  • 2. ByteBuf 分类
  • 3. 代码实例
    • 3.1 常用方法
      • 3.1.1 创建ByteBuf
      • 3.1.2 写入字节
      • 3.1.3 扩容
        • 3.1.2.1 扩容实例
        • 3.1.2.2 扩容计算新容量代码
      • 3.1.4 读取字节
      • 3.1.5 标记回退
      • 3.1.6 slice
      • 3.1.7 duplicate
      • 3.1.8 CompositeByteBuf
      • 3.1.9 retain & release
        • 3.1.9.1 retain & release
        • 3.1.9.2 Netty TailContext release
    • 3.2 完整实例
  • 4. 参考文献

1.概述

ByteBuf 对字节进行操作

ByteBuf 四个基本属性:

  • readerIndex: 读指针,字节数组,读到哪了
  • writerIndex: 写指针,字节数组,写到哪了
  • maxCapacity:最大容量,字节数组最大容量
  • markedReaderIndex:标记读指针,resetReaderIndex方法可以把readerIndex修改为markedReaderIndex,回退重新读数据
  • markedWriterIndex: 标记写指针,resetReaderIndex方法可以把 writerIndex 修改为markedWriterIndex,回退重新写数据
public abstract class AbstractByteBuf extends ByteBuf {int readerIndex;int writerIndex;private int markedReaderIndex;private int markedWriterIndex;private int maxCapacity;
}

2. ByteBuf 分类

ByteBuf 分为

  • 直接内存或堆内存(Heap/Direct)
  • 池化 和 非池化(Pooled/Unpooled)和 操作方式是否安全 (Unsafe/非 Unsafe)

ByteBuf 创建可以基于直接内存或堆内存

  • 直接内存创建和销毁的代价昂贵,但读写性能高(少一次内存复制),适合配合池化功能一起用
  • 直接内存对 GC 压力小,因为这部分内存不受 JVM 垃圾回收的管理,但也要注意及时主动释放

ByteBuf 池化 和 非池化

  • 没有池化,则每次都得创建新的 ByteBuf 实例,这个操作对直接内存代价昂贵,就算是堆内存,也会增加 GC 压力
  • 有了池化,则可以重用池中 ByteBuf 实例,并且采用了与 jemalloc 类似的内存分配算法提升分配效率
  • 高并发时,池化功能更节约内存,减少内存溢出的可能

ByteBuf 操作方式是否安全 (Unsafe/非 Unsafe)

  • Unsafe:表示每次调用 JDK 的 Unsafe 对象操作物理内存,依赖 offset + index 的方式操作数据
  • 非 Unsafe:则不需要依赖 JDK 的 Unsafe 对象,直接通过数组下标的方式操作数据

3. 代码实例

3.1 常用方法

3.1.1 创建ByteBuf

创建ByteBuf , 默认都是池化的

        // 堆内存的ByteBufByteBuf bufferHeap = ByteBufAllocator.DEFAULT.heapBuffer();// 直接内存的ByteBufByteBuf bufferDirect = ByteBufAllocator.DEFAULT.directBuffer();System.out.println(bufferHeap);System.out.println(bufferDirect);

在这里插入图片描述

3.1.2 写入字节

        bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});print("第一次写入", bufferHeap);print("第一次写入", bufferDirect);

在这里插入图片描述

3.1.3 扩容

3.1.2.1 扩容实例
  • 默认 256
  • 扩容加一倍
  • 到了4194304,每次+4194304
      for (int i = 0; i < 100; i++) {bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});}print("批量写入&扩容", bufferHeap);print("批量写入&扩容", bufferDirect);

在这里插入图片描述

3.1.2.2 扩容计算新容量代码
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {ObjectUtil.checkPositiveOrZero(minNewCapacity, "minNewCapacity");if (minNewCapacity > maxCapacity) {throw new IllegalArgumentException(String.format("minNewCapacity: %d (expected: not greater than maxCapacity(%d)", minNewCapacity, maxCapacity));} else {int threshold = 4194304;if (minNewCapacity == 4194304) {return 4194304;} else {int newCapacity;if (minNewCapacity > 4194304) {newCapacity = minNewCapacity / 4194304 * 4194304;if (newCapacity > maxCapacity - 4194304) {newCapacity = maxCapacity;} else {newCapacity += 4194304;}return newCapacity;} else {for(newCapacity = 64; newCapacity < minNewCapacity; newCapacity <<= 1) {}return Math.min(newCapacity, maxCapacity);}}}
}

3.1.4 读取字节

        readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);

在这里插入图片描述

3.1.5 标记回退

        bufferHeap.markReaderIndex();bufferDirect.markReaderIndex();readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);System.out.println("回退");bufferHeap.resetReaderIndex();bufferDirect.resetReaderIndex();readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);

在这里插入图片描述

3.1.6 slice

    // 无参 slice 是从原始 ByteBuf 的 read index 到 write index 之间的内容进行切片// slice 和 bufferHeap 共享一块内存ByteBuf slice = bufferHeap.slice();slice.setByte(0, 9);print("slice", slice);readByte(bufferHeap);

在这里插入图片描述

3.1.7 duplicate

        // 内存拷贝不共享内存ByteBuf duplicate = bufferHeap.duplicate();print("duplicate", duplicate);print("bufferHeap", bufferHeap);duplicate.writeBytes(new byte[]{5});print("duplicate", duplicate);print("bufferHeap", bufferHeap);

在这里插入图片描述

3.1.8 CompositeByteBuf

        // CompositeByteBuf 是一个组合的 ByteBuf,它内部维护了一个 Component 数组,// 每个 Component 管理一个 ByteBuf,记录了这个 ByteBuf 相对于整体偏移量等信息,代表着整体中某一段的数据。// 优点,对外是一个虚拟视图,组合这些 ByteBuf 不会产生内存复制// 缺点,复杂了很多,多次操作会带来性能的损耗ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5);buf1.writeBytes(new byte[]{1, 2, 3, 4, 5});ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5);buf2.writeBytes(new byte[]{6, 7, 8, 9, 10});CompositeByteBuf buf3 = ByteBufAllocator.DEFAULT.compositeBuffer();// true 表示增加新的 ByteBuf 自动递增 write index, 否则 write index 会始终为 0buf3.addComponents(true, buf1, buf2);print("buf3", buf3);

在这里插入图片描述

3.1.9 retain & release

3.1.9.1 retain & release

Netty 这里采用了引用计数法来控制回收内存,每个 ByteBuf 都实现了 ReferenceCounted 接口

  • 每个 ByteBuf 对象的初始计数为 1
  • 调用 release 方法计数减 1,如果计数为 0,ByteBuf 内存被回收
  • 调用 retain 方法计数加 1,表示调用者没用完之前,其它 handler 即使调用了 release 也不会造成回收
  • 当计数为 0 时,底层内存会被回收,这时即使 ByteBuf 对象还在,其各个方法均无法正常使用
        bufferHeap.retain();bufferDirect.retain();bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);

在这里插入图片描述

3.1.9.2 Netty TailContext release

io.netty.channel.DefaultChannelPipeline.TailContext

io.netty.channel.DefaultChannelPipeline.TailContext#channelRead

        @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {onUnhandledInboundMessage(ctx, msg);}

io.netty.channel.DefaultChannelPipeline#onUnhandledInboundMessage(ChannelHandlerContext, Object)

    protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {onUnhandledInboundMessage(msg);if (logger.isDebugEnabled()) {logger.debug("Discarded message pipeline : {}. Channel : {}.",ctx.pipeline().names(), ctx.channel());}}

3.2 完整实例


import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;public class ByteBufStudy {public static void main(String[] args) {// 堆内存的ByteBufByteBuf bufferHeap = ByteBufAllocator.DEFAULT.heapBuffer();// 直接内存的ByteBufByteBuf bufferDirect = ByteBufAllocator.DEFAULT.directBuffer();System.out.println(bufferHeap);System.out.println(bufferDirect);bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});print("第一次写入", bufferHeap);print("第一次写入", bufferDirect);for (int i = 0; i < 100; i++) {bufferHeap.writeBytes(new byte[]{1, 2, 3, 4});bufferDirect.writeBytes(new byte[]{1, 2, 3, 4});}print("批量写入&扩容", bufferHeap);print("批量写入&扩容", bufferDirect);readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);bufferHeap.markReaderIndex();bufferDirect.markReaderIndex();readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);System.out.println("回退");bufferHeap.resetReaderIndex();bufferDirect.resetReaderIndex();readByte(bufferHeap);readByte(bufferDirect);print("读取一个字节", bufferHeap);print("读取一个字节", bufferDirect);// 无参 slice 是从原始 ByteBuf 的 read index 到 write index 之间的内容进行切片// slice 和 bufferHeap 共享一块内存ByteBuf slice = bufferHeap.slice();slice.setByte(0, 9);print("slice", slice);readByte(bufferHeap);// 内存拷贝不共享内存ByteBuf duplicate = bufferHeap.duplicate();print("duplicate", duplicate);print("bufferHeap", bufferHeap);duplicate.writeBytes(new byte[]{5});print("duplicate", duplicate);print("bufferHeap", bufferHeap);// CompositeByteBuf 是一个组合的 ByteBuf,它内部维护了一个 Component 数组,// 每个 Component 管理一个 ByteBuf,记录了这个 ByteBuf 相对于整体偏移量等信息,代表着整体中某一段的数据。// 优点,对外是一个虚拟视图,组合这些 ByteBuf 不会产生内存复制// 缺点,复杂了很多,多次操作会带来性能的损耗ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5);buf1.writeBytes(new byte[]{1, 2, 3, 4, 5});ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5);buf2.writeBytes(new byte[]{6, 7, 8, 9, 10});CompositeByteBuf buf3 = ByteBufAllocator.DEFAULT.compositeBuffer();// true 表示增加新的 ByteBuf 自动递增 write index, 否则 write index 会始终为 0buf3.addComponents(true, buf1, buf2);print("buf3", buf3);bufferHeap.retain();bufferDirect.retain();bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);bufferHeap.release();bufferDirect.release();print("release bufferHeap", bufferHeap);print("release bufferDirect", bufferDirect);}public static void print(String prefix, ByteBuf buffer) {System.out.printf("%s readerIndex : %s writerIndex : %s maxCapacity : %s capacity : %s %n",prefix, buffer.readerIndex(), buffer.writerIndex(), buffer.maxCapacity(), buffer.capacity());}public static void readByte(ByteBuf buffer) {System.out.printf("读取一个字节: %s %n", buffer.readByte());}
}

4. 参考文献

  • 黑马 Netty教程
  • 拉钩教育 Netty课程 若地老师
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jmeter5.X性能测试【完整版】

目录 一、Http基础协议和解析 1、浏览器的B/S架构和C/S架构 &#xff08;1&#xff09;CS架构 &#xff08;2&#xff09;BS架构 &#xff08;3&#xff09;URL理解 2、Http超文本传输协议 &#xff08;1&#xff09;含义 # 协议 # json协议 # xml协议 &#xff08;…

分布式锁(Redission)

分布式锁&#xff1a; 使用场景&#xff1a; 通常对于一些使用率高的服务&#xff0c;我们会进行多次部署&#xff0c;可能会部署在不同的服务器上&#xff0c;但是他们获取和操作的数据仍然是同一份。为了保证服务的强一致性&#xff0c;我们需要对线程进行加锁&#xff0c;…

WRONGPASS invalid username-password pair or user is disabled

连接redis客户端的时候报错&#xff1a;WRONGPASS invalid username-password pair or user is disabled 当前redis版本为7.2.4 一、问题分析 默认情况下&#xff0c;Redis 7.0 使用默认用户名 default 和空密码进行身份验证。如果未设置 requirepass&#xff0c;则默认用户名…

C++实现自动生成c++类中的属性的get和set方法

目录 应用场景 运行准备 代码展示 结果显示 应用场景 当我们在编写类的属性时&#xff0c;需要对该属性进行封装&#xff0c;需要一系列的get和set的方法。例如下面是天气类的成员属性。可以看到属性很多&#xff0c;而写get和set都是一些固定的操作&#xff0c;因此可以直…

任务4.8.4 利用Spark SQL实现分组排行榜

文章目录 1. 任务说明2. 解决思路3. 准备成绩文件4. 采用交互式实现5. 采用Spark项目实战概述&#xff1a;使用Spark SQL实现分组排行榜任务背景任务目标技术选型实现步骤1. 准备数据2. 数据上传至HDFS3. 启动Spark Shell或创建Spark项目4. 读取数据5. 数据转换6. 创建临时视图…

哈夫曼树及其应用

目录 一、哈夫曼树 1.1基本概念 1.2构造方法 1.3构造算法的实现 二、哈夫曼树的应用 2.1哈夫曼编码 2.2文件的编码和解码 2.2.1编码 2.2.2解码 一、哈夫曼树 1.1基本概念 哈夫曼树又称为最优树&#xff0c;是一类带权路径长度最短的树。 最优二叉树&#xff1a;带权…

Android内核编译

前言 本文描述使用Ubuntu 编译Android内核刷入pixel4一些心得和流程。 PC信息&#xff1a; ./o- jackjackyyyyy- -yyyyyy OS: Ubuntu 22.04 jammy:////-yyyyyyo Kernel: x86_64 Linux 6.5.0-35-generic. .://-.sss/ Uptime: 1d 5h 4m.:o: //:--:/- …

C++入门 vector部分模拟实现

目录 vector大致框架 vector常见接口模拟实现 begin迭代器 & end迭代器 capacity( ) & size( ) reserve operator[ ] push_back( ) & pop_back( ) sort vector大致框架 vector的内部的成员变量大概有三部分构成&#xff1a; namespace bit {template<c…

【Java算法】滑动窗口 上

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【算法工作坊】算法实战揭秘 &#x1f456;一. 长度最小的子数组 题目链接&#xff1a;209.长度最小的子数组 算法原理 滑动窗口 滑动窗口算法常用于处理数组/字符串等序列问题&#xff0c;通过定义一…

Java赋值运算符

Java赋值运算符分为以下&#xff1a; 符号 作用 说明 赋值 int a 10,把10赋值给变量a 加后赋值 ab,将ab的值赋值给变量a - 减后赋值 a-b,将a-b的值赋值给变量a* 乘后赋值 a*b,将a*b的值赋值给变量a / 除后赋值 a/b,将a/b的值赋值给变量a % 取余赋值 a%b,将a%b的值赋值给变量…

贪心算法—

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法。这种算法并不总是能找到全局最优解&#xff0c;但在某些问题上能提供足够好的解决方案。贪心算法的关键特性包括&#…

JR-8000系列机架式多路4K超高清光端机

集中式 4K超高清光传输设备 1 产品特性 ⚫ 支持高达 8 通道 SMPTE 全格式 SDI 信号输入 ⚫ 发送端带有 LOOPOUT 环出端口&#xff0c;具备消抖动功能&#xff0c;可作为信号调理或级联信号源使用 ⚫ 接收端支持双输出端口 ⚫ 支持传输速率&#xff1a;143Mbps-11.88Gbps ⚫…

Intel太无耻,跟着玩数字游戏还揭台积电的老底,工艺都是假的

在台积电的3纳米逐渐获得芯片企业认可的情况下&#xff0c;近日Intel却再次指出台积电的3纳米工艺并非真正的3纳米&#xff0c;与Intel的7纳米工艺差不多&#xff0c;这显示出Intel在芯片工艺研发方面日益落后的情况下确实有点慌了。 Intel指出它的7纳米工艺的晶体管密度达到1.…

python watchdog 配置文件热更新

目录 一、Watchdog示例 二、aiohttp服务配置热更新 在同事的golang代码中学习到了config.json热更新的功能&#xff0c;这里自己也学习了一下python写web服务的时候怎么来实现配置的热更新。主要是利用Watchdog这个第三方python库&#xff0c;来监控文件系统的改变&#xff0…

学习使用venv创建“python虚拟环境”

前言 使用python开发会经常面临的问题是&#xff1a;你会需要不同版本的python&#xff0c;而且就算同一个版本的python&#xff0c;不同的项目有很大可能会需要不同版本的包。而 “Python虚拟环境” 就是为了解决这个问题的。 目标 结合官方文档&#xff0c;自己动手实践来…

开启声音的奇幻之旅:AI声音变换器的魔法秘籍与创意应用

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/这个充满科技魔力的时代&#xff0c;AI Voice Changer 就像一把神奇的钥匙&#xff0c;能为我们打开声音的魔法之门。今天&#xff0c;就让我带你…

JetBrains PyCharm 2024 mac/win版编程艺术,智慧新篇

JetBrains PyCharm 2024是一款功能强大的Python集成开发环境(IDE)&#xff0c;专为提升开发者的编程效率和体验而设计。这款IDE不仅继承了前代版本的优秀特性&#xff0c;还在多个方面进行了创新和改进&#xff0c;为Python开发者带来了全新的工作体验。 JetBrains PyCharm 20…

腰背肌筋膜炎怎么治疗最有效

腰背肌筋膜炎的治疗方法主要包括以下几种&#xff1a; 1、休息和物理治疗&#xff1a; 确保充足的休息&#xff0c;避免过度劳累&#xff0c;减少腰背部肌肉的负担。 物理治疗&#xff0c;如热敷或冷敷&#xff0c;可以缓解疼痛和肌肉紧张。热敷可以使用热水袋、热毛巾或电热垫…

linux普通: rocketmq的安装测试与可视化界面安装,git的 (linux) 安装

全文目录,一步到位 1.前言简介1.1 专栏传送门(rabbitmq) 2. rocketmq使用及安装2.0 开放端口2.1 rocketmq版本说明2.2 具体操作2.2.1 修改文件2.2.2 具体启动指令ps: 查看日志 2.3.3 jps查看java进程2.3.4 测试运行情况> 步骤一: 临时指定nameserver注册中心位置> 步骤二…

【机器学习】基于Softmax松弛技术的离散数据采样

1.引言 1.1.离散数据采样的意义 离散数据采样在深度学习中起着至关重要的作用&#xff0c;它直接影响到模型的性能、泛化能力、训练效率、鲁棒性和解释性。 首先&#xff0c;采样方法能够有效地平衡数据集中不同类别的样本数量&#xff0c;使得模型在训练时能够更均衡地学习…