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

📅 2026/6/20 13:16:30
【Netty源码解读和权威指南】第39篇:Netty内存泄漏检测机制源码解析——守护ByteBuf的“生死账本“
上一篇【第38篇】Netty SSL TLS安全传输——HTTPS背后的Netty实现下一篇【第40篇】Netty内存管理深度解析——PoolChunk/PoolArena源码全剖析一、内存泄漏的根源// 典型泄漏忘记release()ByteBufbufctx.alloc().buffer(1024);buf.writeBytes(data);ctx.writeAndFlush(buf);// 忘记 buf.release()内存泄漏Netty使用引用计数法每分配一个ByteBufrefCnt1。每次retain()refCnt1。每次release()refCnt-1。当refCnt0时释放内存。二、ResourceLeakDetector工作原理// 核心使用虚引用(PhantomReference)跟踪publicclassResourceLeakDetectorT{// 检测级别publicenumLevel{DISABLED,// 禁用SIMPLE,// 1%采样只检测是否泄漏ADVANCED,// 记录访问栈定位泄漏位置PARANOID// 100%检测性能损失大}// 创建可跟踪对象publicResourceLeakopen(Tobj){if(levelLevel.DISABLED)returnnull;if(level.ordinal()Level.PARANOID.ordinal()){if(random.nextInt(samplingInterval)!0)returnnull;// 采样}// 创建DefaultResourceLeak用WeakReference追踪DefaultResourceLeakleaknewDefaultResourceLeak(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.levelPARANOID-Dio.netty.leakDetection.targetRecords8// Java代码配置ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);级别选择建议级别性能影响使用场景DISABLED无生产环境确认无泄漏后SIMPLE极小(1%采样)生产环境常规监控ADVANCED中等预发布环境PARANOID大(100%检测)开发/测试环境五、泄漏日志解读LEAK: ByteBuf.release() was not called before its 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源码全剖析