java生产环境内存持续飙升需分层排查:先用ps、jstat判断是否堆内泄漏,再查metaspace、direct memory、容器日志及线程数等非堆因素,最后通过jmap、mat定位泄漏对象并闭环验证。

Java生产环境内存持续飙升,核心是对象无法被回收或非堆内存异常增长。排查要分层推进:先确认是不是JVM堆内问题,再看是否容器/系统层面干扰,最后深入代码和配置。
快速确认内存飙升类型
先用基础命令判断现象性质:
-
ps -p
-o %mem,rss :看RSS(实际物理内存)是否持续上涨;若RSS涨但堆使用率(jstat -gc里的OU/OC)不涨,说明问题在堆外(如Direct Buffer、线程栈、Metaspace或Linux page cache) -
jstat -gc
1s :观察FGC是否频繁、OU是否单向增长、GCT是否飙升;若FGC后OU几乎不下降,极大概率存在内存泄漏 - free -h && cat /proc/meminfo | grep -E "Cached|SReclaimable|Shmem":若Cached或Shmem异常高,可能是日志文件、临时IO缓存占用了大量页缓存(常见于容器中未轮转的大日志)
定位堆内泄漏对象
一旦确认是堆内存问题,立即抓取存活对象快照:
-
jmap -histo:live
| head -20 :直接列出数量最多、占用内存最大的前20个类;重点关注byte[]、String、HashMap$Node、自定义缓存类、第三方框架实体类(如Hibernate Proxy、Netty ByteBuf) -
jmap -dump:format=b,file=heap.hprof
:生成堆转储;建议加 -J-Xmx2g防止导出过程OOM(jmap -dump:format=b,file=heap.hprof -J-Xmx2g <pid></pid>) - 用MAT打开hprof文件,查看Dominator Tree和Leak Suspects Report;若发现某类实例数达数十万且Retained Heap巨大,基本就是泄漏源头
检查非堆与系统级干扰
很多“内存飙升”其实和JVM堆无关:
-
Metaspace泄漏:执行
jstat -gc <pid></pid>看MC/MU是否持续增长;微服务频繁热部署、动态代理(如Spring AOP)、字节码增强(如SkyWalking)易导致类加载器泄漏;可加-XX:MaxMetaspaceSize=512m并监控 -
Direct Memory泄漏:Netty、NIO程序常见;通过
jcmd <pid> VM.native_memory summary</pid>查看Internal和Direct部分;若Direct持续上涨,检查ByteBuffer.allocateDirect()是否未清理 -
容器内日志膨胀:容器内存 = 进程RSS + page cache;
du -sh /var/log/containers/*.log或应用日志目录,常有GB级未轮转日志;清空后内存立刻回落即为实锤 -
线程数爆炸:每个线程默认栈大小1MB(-Xss),
ps -T -p <pid> | wc -l</pid>超千条需警惕;结合jstack <pid></pid>查是否有线程创建未回收(如忘记shutdown ExecutorService)
验证与收口
找到嫌疑点后,不要只看现象,要闭环验证:
- 对疑似泄漏代码,加监控埋点:统计缓存size、连接池active数、监听器注册数等,在Prometheus暴露为Gauge指标
- 复现场景做对比测试:同一请求压测前后,分别执行
jmap -histo:live,看特定类实例增量是否匹配业务逻辑 - 上线后开启GC日志:
-Xlog:gc*:file=gc.log:time,uptime,level,tags,用GCEasy分析Full GC是否消失、堆使用趋势是否平稳
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











