【Netty源码解读和权威指南】第39篇:Netty内存泄漏检测机制源码解析——守护ByteBuf的“生死账本“
上一篇【第38篇】Netty SSL TLS安全传输——HTTPS背后的Netty实现
下一篇【第40篇】Netty内存管理深度解析——PoolChunk/PoolArena源码全剖析
一、内存泄漏的根源
// 典型泄漏:忘记release()ByteBufbuf=ctx.alloc().buffer(1024);buf.writeBytes(data);ctx.writeAndFlush(buf);// 忘记 buf.release()!内存泄漏!Netty使用引用计数法:每分配一个ByteBuf,refCnt=1。每次retain(),refCnt+1。每次release(),refCnt-1。当refCnt=0时释放内存。
二、ResourceLeakDetector工作原理
// 核心:使用虚引用(PhantomReference)跟踪publicclassResourceLeakDetector<T>{// 检测级别publicenumLevel{DISABLED,// 禁用SIMPLE,// 1%采样,只检测是否泄漏ADVANCED,// 记录访问栈,定位泄漏位置PARANOID// 100%检测,性能损失大}// 创建可跟踪对象publicResourceLeakopen(Tobj){if(level==Level.DISABLED)returnnull;if(level.ordinal()<Level.PARANOID.ordinal()){if(random.nextInt(samplingInterval)!=0)returnnull;// 采样}// 创建DefaultResourceLeak,用WeakReference追踪DefaultResourceLeakleak=newDefaultResourceLeak(obj);allLeaks.put(leak,Boolean.TRUE);returnleak;}}三、泄漏检测流程
创建ByteBuf → ResourceLeakDetector.open() ↓ 创建DefaultResourceLeak(虚引用) ↓ ByteBuf.release() → leak.close() ↓ ↓ 正确释放 忘记release() → GC回收ByteBuf对象 ↓ ↓ leak从Map移除 虚引用进入ReferenceQueue ↓ ResourceLeakDetector检测到! ↓ 打印日志:LEAK: ByteBuf was not released!四、实战配置
// JVM参数配置-Dio.netty.leakDetection.level=PARANOID-Dio.netty.leakDetection.targetRecords=8// Java代码配置ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);级别选择建议:
| 级别 | 性能影响 | 使用场景 |
|---|---|---|
| DISABLED | 无 | 生产环境确认无泄漏后 |
| SIMPLE | 极小(1%采样) | 生产环境常规监控 |
| ADVANCED | 中等 | 预发布环境 |
| PARANOID | 大(100%检测) | 开发/测试环境 |
五、泄漏日志解读
LEAK: ByteBuf.release() was not called before it's garbage-collected. Recent access records: #1: io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:355) #2: com.example.MyHandler.channelRead(MyHandler.java:23) ↑ 泄漏位置!六、总结
| 机制 | 实现 |
|---|---|
| 追踪方式 | PhantomReference + ReferenceQueue |
| 检测级别 | DISABLED/SIMPLE/ADVANCED/PARANOID |
| 采样机制 | SIMPLE/ADVANCED按比例采样 |
| 定位泄漏 | ADVANCED记录访问栈 |
上一篇【第38篇】Netty SSL TLS安全传输——HTTPS背后的Netty实现
下一篇【第40篇】Netty内存管理深度解析——PoolChunk/PoolArena源码全剖析
