JVM 内存泄漏排查:先确认增长曲线,再抓堆
JVM 内存泄漏排查先确认增长曲线再抓堆一、内存升高不一定是泄漏线上看到 JVM 内存上涨很多人第一反应是抓堆、调大内存或怀疑缓存泄漏。但内存升高不等于泄漏。可能是流量增长、缓存预热、批量任务、GC 策略变化、直接内存占用或线程栈增多。排查内存问题要先看趋势和分区不能一上来就下结论。真正的泄漏通常表现为 Full GC 后老年代仍持续上涨或者某类对象数量长期只增不减。确认这个趋势后再抓 heap dump 才有意义。否则抓到的只是某个时刻的正常对象。二、排查链路监控、GC、堆 dump 逐步推进flowchart TD A[内存告警] -- B[查看趋势] B -- C[分析 GC 日志] C -- D{Full GC 后是否下降} D --|下降| E[容量或流量问题] D --|不下降| F[抓 Heap Dump] F -- G[对象引用分析]先看堆内和堆外。堆内问题可以通过 GC 日志和 heap dump 分析堆外可能来自 Netty direct buffer、mmap、JNI 或线程栈。只看-Xmx不够。容器环境里还要看进程 RSS 和 cgroup 限制避免被 OOMKill。GC 日志是第一证据。观察 Young GC、Full GC、老年代使用率、停顿时间和回收后大小。如果每次 Full GC 后老年代都能回落可能是压力大而非泄漏如果回收后基线持续抬升就要深入分析。三、命令示例先拿轻量证据下面是常用排查命令线上执行要注意权限和影响。jcmd pid GC.heap_info jcmd pid VM.native_memory summary jstat -gcutil pid 1000 10 jcmd pid GC.class_histogramGC.class_histogram比 heap dump 轻一些可以先看对象数量排名。若某个业务对象、集合或 byte array 异常多再考虑抓 dump。抓 dump 可能造成停顿和磁盘压力核心线上环境要谨慎最好在故障实例或下线实例执行。分析 heap dump 时重点看 dominator tree 和 GC roots。找到大对象不够还要知道是谁持有它。很多泄漏来自静态 Map、未清理监听器、ThreadLocal、缓存无上限和队列堆积。四、常见根因缓存和 ThreadLocal 很常见缓存泄漏通常不是缓存本身有问题而是没有容量、TTL 或淘汰策略。业务一开始数据少缓存看起来正常流量起来后key 数量持续增长。所有本地缓存都应该有最大容量和监控。ThreadLocal 泄漏在 Web 服务里也常见。线程池线程长期存活如果请求结束后没有 remove数据会一直挂在线程上。特别是保存用户上下文、大对象或临时缓冲区时要在 finally 中清理。最后修复后要观察曲线。内存泄漏不是改完代码就结束要看 Full GC 后基线是否稳定、对象数量是否停止增长、P99 延迟是否恢复。没有验证修复只是猜测。还要把内存问题和发布记录对齐。很多泄漏来自某次新功能引入的缓存、监听器或批处理队列。把老年代基线、Full GC 次数和版本发布时间放在同一张图上通常比盲目翻代码更快。排查不是靠英雄主义是靠证据把范围缩小。五、总结JVM 内存泄漏排查要先确认增长曲线和 GC 后基线再抓堆分析引用链。区分堆内堆外、缓存增长、ThreadLocal、队列堆积和容量问题。证据顺序对了排查才不会变成盲目抓 dump。