heap dump无法分析堆外内存泄漏,因其仅捕获java堆内对象,不包含directbytebuffer等native内存;需用nmt、arthas或jcmd直查direct内存增长及调用栈。

Heap Dump 本身无法直接分析堆外内存泄漏——它只反映 Java 堆内对象的引用关系与存活状态。如果你在 heapdump.hprof 里拼命找大对象、查 GC Roots,却始终没发现明显泄漏源,那大概率问题不在堆内,而你正在用错工具。
为什么 jmap -dump 对堆外泄漏基本无效
Heap Dump 是 JVM 在运行时对 Java heap 的快照,不包含:DirectByteBuffer 底层分配的 native 内存页、Unsafe.allocateMemory 返回的地址、JNI 分配的 C 堆内存、Netty PooledByteBuf 的 arena chunk 等。这些全部游离在 GC 控制之外,jmap 根本不感知。
常见误判现象:
- 堆内存使用稳定(
O列长期 60%~70%,FGC频次低),但ps aux显示 RSS 持续涨到 8G+; - MAT 分析
Dominator Tree找不到超大对象,Histogram中java.nio.DirectByteBuffer实例数也不多(比如仅几百个); - 重启后 RSS 立即回落,但 1 小时内又回到高位——说明泄漏是运行时持续发生的,不是启动期静态分配。
Heap Dump 能间接帮上什么忙
虽然不能看堆外,但能帮你排除干扰、缩小怀疑范围:
- 确认
DirectByteBuffer实例是否被意外长期持有:检查它的cleaner字段是否还挂载着未执行的Cleaner,或其att(attachment)是否引用了大对象(如未关闭的SocketChannel); - 排查“伪堆外泄漏”:某些框架(如早期 Druid)会把连接池中的
PooledConnection设为 static,导致底层Socket和关联的DirectBuffer无法释放;这类问题在堆内有强引用链,MAT 的Path to GC Roots能定位; - 验证是否真与堆无关:如果
jstat -gcutil显示O(老年代)使用率缓慢爬升且 Full GC 无效,才值得回头再查堆;否则优先转向堆外。
真正该用的堆外诊断命令组合
绕过 Heap Dump,直击 native 层:
- 启动时加参数:
-XX:NativeMemoryTracking=detail(注意:预发可用,生产慎开,性能损耗 5%~10%); - 运行中采样:
jcmd $PID VM.native_memory summary scale=KB,重点看Internal、Direct、Mapped三栏是否持续增长; - 对比两次快照差值:
jcmd $PID VM.native_memory baseline后等 10 分钟,再执行VM.native_memory summary diff; - 若看到
Direct增长快,立刻查:jcmd $PID VM.native_memory detail | grep -A 5 -B 5 "Direct",看是否来自java.nio.Bits.reserveMemory调用栈。
Netty / NIO 场景下最易忽略的泄漏点
很多团队以为关了 Channel 就万事大吉,其实:
-
ByteBuf.release()必须显式调用,尤其在ChannelInboundHandler.channelRead()异常分支里遗漏; -
DefaultChannelPromise若未监听或未setFailure,其内部value可能持有一个未释放的ByteBuf; - GraalVM Native Image 下,
io.netty.allocator.type=pooled必须配合-Dio.netty.noPreferDirect=true,否则默认走Unpooled+Direct,泄漏更隐蔽; - Spring WebFlux 默认启用
Reactor Netty,其HttpClient.create().option(ChannelOption.SO_KEEPALIVE, true)若未配置连接池最大空闲时间,DirectByteBuffer会长期滞留在连接池中。
堆外泄漏的麻烦在于:它不报 OOM 错误,只默默吃掉 RSS,直到容器被 OOMKilled 才暴露。而一旦开启 NMT,VM.native_memory 输出里的 Unknown 类别占比过高,就说明有大量 native 分配未被 JVM 追踪到——这时得祭出 valgrind --tool=massif 或 GraalVM 的 --trace-class-initialization,而不是继续翻 heapdump。










