核心是盯住堆内存是否持续上涨、gc是否频繁无效、对象是否该回收却没回收;通过jps定位pid,jstat监控ou/ygc/fgc,jmap导出堆快照用mat分析dominator tree和leak suspects,结合gc日志观察full gc后old gen回落情况及gc overhead指标。

JVM 内存使用性能监测分析,核心是盯住“堆内存是否持续上涨”“GC 是否频繁无效”“对象是否该回收却没回收”这三件事。不靠猜,靠数据;不靠经验,靠工具输出的真实指标。
看进程和内存分布:先找到目标,再看结构
第一步永远是确认你在监控哪个 Java 进程:
- 运行 jps -l,列出所有 Java 进程及其主类名,快速定位你的服务 PID(比如 12345 com.example.Application)
- 接着用 jstat -gc 12345 1000 5 每秒采样一次、共 5 次,重点关注:
- OU(OldUsed):老年代已用空间百分比——持续 >90% 且 Full GC 后不下降,高度可疑
- YGC/YGCT 和 FGC/FGCT:年轻代/Full GC 次数与耗时——FGC 频次上升 + FGCT 增长,说明老年代压力大
抓堆快照查对象堆积:定位谁在吃内存
当发现 OU 持续攀升或 OOM 频发,就要导出堆现场:
- 执行 jmap -dump:live,format=b,file=heap.hprof 12345(加
live只 dump 存活对象,更贴近真实泄漏场景) - 用 Eclipse MAT 打开 heap.hprof,直接看:
-
Dominator Tree:排第一的“大对象持有者”,比如
Log4jLogEvent占 40% 堆内存,基本就是日志异步队列堵了 -
Leak Suspects Report:MAT 自动标出疑似泄漏路径,例如某个静态
ConcurrentHashMap持有数千个未清理的 Handler 实例
-
Dominator Tree:排第一的“大对象持有者”,比如
结合 GC 日志看回收实效:判断是不是真泄漏
光看堆大小不够,得看 GC 到底有没有效:
- 启动时加参数:-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
- 观察关键信号:
- 每次 Full GC 后 Old Gen 使用量没明显回落(比如从 980MB → 975MB),说明有对象“卡住”没被回收
- 出现 “GC overhead limit exceeded”,代表 JVM 把近 98% 时间花在 GC 上却收效甚微,是泄漏的强信号
- 年轻代 GC 后 Eden 区几乎清空,但 Survivor 区对象越积越多 → 可能是对象晋升过快或 Survivor 空间太小
日常监控要盯住两个基线指标
不是等崩了才查,而是建立常态观察习惯:
- 堆内存锯齿曲线底部是否缓慢抬升:正常 GC 后应回落到相近低点;若每次最低点都比上次高 5–10MB,大概率存在缓慢泄漏
- Full GC 间隔时间是否持续缩短:从几小时→几十分钟→几分钟,说明老年代填充速度远超回收能力











