【Netty源码解读和权威指南】第39篇:Netty内存泄漏检测机制源码解析——守护ByteBuf的“生死账本“

【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源码全剖析