jvm gc性能监控核心是观察内存分配、对象流动与回收健康度,关键指标为分配速率、晋升速率、young gc频率与停顿稳定性、老年代剩余空间趋势;需结合jstat、gc日志分析及prometheus等工具分场景监控,并依现象反推调优。

JVM GC 机制下的应用性能监控,核心不是“看GC有没有发生”,而是看内存如何被分配、对象如何流动、回收是否健康。重点在于用可观测数据判断系统是否处于可持续运行状态,而不是盯着某次停顿或某个百分比。
关键指标要盯住这四个
真正影响服务稳定性的,是以下四类指标的组合趋势:
- 分配速率(Allocation Rate):单位时间新对象创建量(MB/s)。过高说明短生命周期对象泛滥,比如循环中反复 new 对象、字符串拼接未用 StringBuilder;建议控制在 10–50 MB/s(视堆大小而定),超过 100 MB/s 就需排查代码
- 晋升速率(Promotion Rate):每秒进入老年代的对象大小。若接近分配速率,说明 Survivor 区太小或对象年龄过早晋升;持续上升且老年代使用率同步爬升,大概率存在内存泄漏或缓存未清理
- Young GC 频率与停顿稳定性:例如 Eden 区 512MB,每 3 秒填满 → 每 3 秒一次 Minor GC;若停顿从 15ms 慢慢涨到 60ms,可能是 Survivor 空间不足、对象年龄分布异常,或 CMS/G1 的 Mixed GC 触发条件被频繁满足
- 老年代剩余空间趋势:每次 Full GC 后,老年代应明显回落(如从 92% → 45%);如果 GC 后仍维持在 85% 以上,或每次只下降 1–2%,说明有长期存活对象堆积,或存在隐形泄漏(如静态 Map 缓存未清除)
监控工具选对才不白忙
不同场景用不同工具,避免过度依赖单一手段:
-
实时排查(线上紧急):用
jstat -gc <pid> 2000</pid>每 2 秒刷新一次,重点关注YGC/YGCT(年轻代次数/耗时)、FGC/FGCT(Full GC 次数/耗时)、OU(老年代已用)和GCT(总 GC 时间占比)。若 GCT > 5%,说明 GC 已开始吃掉有效 CPU 时间 -
深度分析(问题复现后):开启结构化 GC 日志(JDK8:
-XX:+PrintGCDetails -Xloggc:/logs/gc.log;JDK11+:-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags),再用 GCViewer 或 GCEasy 做可视化分析,识别停顿毛刺、内存碎片、混合回收效率等 -
长期趋势(生产环境):集成 Stagemonitor 或 Prometheus + JMX Exporter,采集
java.lang:type=GarbageCollector下的CollectionCount、CollectionTime、LastGcInfo等 MBean 数据,配合 Grafana 做告警(如 FGC ≥ 2 次/小时、单次 GC 停顿 > 500ms)
别被表面数字带偏节奏
很多团队误判性能问题,是因为只看“堆使用率”或“GC 次数”,忽略了上下文:
- 堆使用率 70% 不一定安全——如果其中老年代占了 93%,Eden 区只剩 10%,那下一次 Young GC 就可能触发晋升失败,进而引发 Full GC
- Young GC 频繁 ≠ 一定有问题——高吞吐业务(如批处理)本就大量创建临时对象,只要停顿稳定、晋升可控,就是健康状态
-
System.gc()调用记录不能作为 GC 健康依据——现代收集器(G1/ZGC)基本忽略它,频繁调用反而干扰 JVM 自适应策略,掩盖真实泄漏点 - 元空间(Metaspace)持续增长且未回落,比堆内存上涨更危险——往往意味着动态类加载(如 Spring CGLIB、热部署框架)未卸载,最终触发
OutOfMemoryError: Metaspace
调优逻辑从现象反推,不是堆参数
遇到问题,按这个顺序查,比盲目改 -Xmn 或 -XX:MaxGCPauseMillis 更可靠:
- 先看
jstat输出中S0U/S1U是否长期接近S0C/S1C?→ Survivor 区不够,调-XX:SurvivorRatio或增大新生代 - 再比对
YGC和FGC的增长斜率:若 FGC 上升更快,检查老年代是否被大对象(如未分页的 List、缓存全量 dump)直接填充 - 用
jmap -histo:live <pid></pid>查 Top 20 类实例数和占用,重点关注char[]、byte[]、HashMap$Node、自定义缓存类——它们常是泄漏源头 - 确认 GC 日志中是否有
Allocation Failure(Young GC 主因)或Metadata GC Threshold(元空间触发)等明确原因字段,避免归因为“内存不够”这种模糊结论











