第五天:深度解密 Netty ByteBuf:高性能 IO 的基石

Netty ByteBuf:直接内存 vs 堆内存

Netty的ByteBuf分配和Java NIOByteBuffer类似

复制代码
// 堆内存
ByteBuf heapBuf = allocator.heapBuffer(10);

// 直接内存
ByteBuf directBuf = allocator.directBuffer(10);

|---------|-------------|----------------|
| 对比项 | 堆内存 ByteBuf | 直接内存 ByteBuf |
| 内存位置 | JVM 堆 | JVM 堆外 |
| GC 管理 | 是 | 否 |
| IO 性能 | 慢(多一次拷贝) | 快(少一次拷贝) |
| 创建成本 | 低 | 高 |
| 是否需手动释放 | 否 | 是(release) |

Direct ByteBuf 提高 IO 性能、降低 GC 压力,但创建成本高且需要手动释放;Heap ByteBuf 管理简单,但 IO 性能差。

按你刚才的要求,继续**极限精简,只保留"有用信息"**👇


Netty ByteBuf:池化 vs 非池化(精简版)

复用 ByteBuf,避免频繁创建 / 销毁的事件耗费

2️⃣ 核心差异

|------------|-------------------|--------|
| 对比 | 非池化 | 池化 |
| ByteBuf 获取 | 每次 new | 从池中复用 |
| 直接内存成本 | 高(频繁 malloc/free) | 低 |
| GC / 内存压力 | 大 | 小 |
| 并发场景 | 不友好 | 友好 |


3️⃣ 启用方式

复制代码
-Dio.netty.allocator.type=pooled   # 池化
-Dio.netty.allocator.type=unpooled # 非池化

4️⃣ 默认策略(记结论)

  • Netty 4.1+ :非 Android 默认 池化
  • Android:默认非池化
  • 4.1 之前:默认非池化(池化不成熟)

池化通过复用 ByteBuf 降低直接内存分配成本和 GC 压力,是 Netty 在高并发 IO 场景下的关键优化手段。

组成

ByteBuf由四部分组成

  1. 读指针(readerIndex):标记当前可读数据的起始位置。
  2. 写指针(writerIndex):标记当前可写数据的起始位置。
  3. 容量(capacity):当前 ByteBuf 实际占用的内存大小。
  4. 最大容量(maxCapacity):ByteBuf 可扩容到的最大内存限制。

相比于ByttBuffer,不用来回切换读写模式更方便,且支持动态扩容

整数大端写入端:更符合读书习惯

整数小端写入: CPU 从低位到高位的运算顺序,内存访问更连续。

ByteBuf扩容规则:

  1. 如果写入字节小于512,则容量下一次为16的整数倍。
  2. 如果写入字节大于512,则容量下一次为2^n
  3. 扩容后不能超过max capacity

内存释放

核心机制:引用计数 (Reference Counting)

Netty 的 ByteBuf 实现了 ReferenceCounted 接口。

  • 初始状态: 创建一个新的 ByteBuf 时,引用计数 refCnt1
  • Retain (保留): 调用 buf.retain(),引用计数 +1。表示有新的地方在使用它。
  • Release (释放): 调用 buf.release(),引用计数 -1
  • 回收: 当引用计数变为 0 时,Netty 会将该内存块归还给内存池(Pooled)或直接销毁(Unpooled)。

警告: 如果引用计数未归零,GC 无法回收堆外内存(Direct Memory),将直接导致 内存泄漏 (Memory Leak) 。如果引用计数归零后继续访问即使ByteBuf对象还在,但是底层内存会被回收,会抛出 IllegalReferenceCountException

释放时机:而在netty中一个基本原则:"谁最后使用(消费)了数据,谁就负责释放。"

Slice;切片

【零拷贝】的体现之一,对原始 ByteBuf 进行切片成多个 ByteBuf, 切片后的 ByteBuf 并没有发生内存复制,还是使用原始 ByteBuf 的内存,切片后的 ByteBuf 维护独立的 read, write 指针

逻辑上对ByteBuf进行切分,其实物理内存还是同一块

ByteBuf.slice(index1,index2)

index1:起始位置

index2:切片长度

切片共享原始内存,容量固定,原 ByteBuf 一旦释放,切片就不可用了;

写入超界会抛异常,安全使用需要正确管理引用计数。

duplicate

【零拷贝】的体现之一,就好比截取了原始 ByteBuf 所有内容,并且没有 max capacity 的限制,也是与原始ByteBuf 使用同一块底层内存,只是读写指针是独立的

零拷贝相反Copy

会将底层内存数据进行深拷贝,无论读写,都与原始ByteBuf无关

Composite

CompositeByteBuf

把多个 ByteBuf 逻辑上拼接成一个连续缓冲区

unPooled

unpooled 是 Netty 提供的一个 工具类,用来创建 非池化(unpooled)的 ByteBuf,区别于 Netty 的 池化 ByteBuf(PooledByteBuf)。它的核心作用和特点可以总结如下:

相关推荐
Yangy_Jiaojiao5 分钟前
具身智能VLA模型全景图:从Pi0到HoloBrain-0的15大开源力作
开源
艾莉丝努力练剑9 分钟前
【QT:解决问题】Qt5Core.dll:无法定位程序输入点
java·开发语言·qt·学习·面试
小蒜学长19 分钟前
“喵汪联盟”宠物领养系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·宠物
程序员黑豆20 分钟前
Java字符串常量池完全指南:原理、intern()方法与性能优化最佳实践
java·前端·ai编程
AI人工智能+电脑小能手23 分钟前
大白话说Java设计模式-14-适配器模式(业务实战篇)
java·设计模式·适配器模式·系统兼容·多渠道对接
xbgRS3 小时前
springboot的自动装配
java·spring boot
嘻哈∠※10 小时前
0061基于 SpringBoot 的投稿与稿件处理系统设计与实现
java·spring boot·后端
白狐_79811 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander25812 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
码匠许师傅12 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试