JVM GC 日志分析实战指南——别再只看 Full GC 次数了
一、GC 问题要看上下文
JVM 排障时,Full GC 次数很显眼,但只看次数不够。一次 Full GC 可能是正常元数据回收,也可能是堆压力失控;年轻代 GC 频繁也可能已经严重影响延迟。GC 日志分析要看暂停时间、回收效果、分配速率和业务延迟。
GC 不是越少越好,而是要和服务目标匹配。
二、先打开可用日志
flowchart TD A[JVM 运行] --> B[GC 日志] B --> C[暂停时间] B --> D[堆变化] B --> E[原因] C --> F[性能判断]JDK 11 之后可以使用统一日志参数。
-Xlog:gc*:file=gc.log:time,uptime,level,tags日志要和应用版本、实例、时间窗口关联,否则事后很难对应业务现象。
三、看回收前后变化
Pause Young (Normal) 512M->180M(1024M) 35ms这类日志要看三个点:暂停类型、回收前后堆大小、暂停时间。年轻代回收后剩余很多,可能对象晋升压力大;Full GC 后仍释放不出空间,可能有内存泄漏或缓存失控。
还要看分配速率。如果业务突然创建大量短生命周期对象,Minor GC 会变频繁。优化方向可能是减少临时对象,而不是盲目加堆。
四、GC 要和业务指标对齐
GC 日志单独看很容易误判。要把暂停时间和接口 P99、线程池队列、CPU 使用率放在同一时间线上。
gc_analysis_context: p99_latency: true allocation_rate: true old_gen_usage: true thread_pool_queue: true如果 GC 暂停和接口抖动时间一致,才有更强证据。否则接口慢可能是下游、锁或网络。
不同 GC 器关注点也不同。G1 要看 region、mixed GC、humongous object;ZGC/Shenandoah 更关注并发阶段和极低暂停目标。不要用一套经验判断所有 GC。
最后,调参前先确认目标。是降低 P99,减少 CPU,还是降低内存成本?目标不同,参数选择也不同。
GC 分析还要关注对象来源。只知道分配速率高不够,要进一步看哪些类在分配。可以结合 JFR、async-profiler 或堆采样工具,找到热点对象。
jcmd <pid> JFR.start name=gc-profile settings=profile duration=120s filename=gc.jfr如果热点来自日志拼接、JSON 序列化、临时集合、重复对象转换,优化代码可能比调 JVM 参数更有效。
还要关注大对象。G1 里的 humongous object 会带来特殊压力,大 byte array、大字符串、大 JSON 都可能触发问题。看到堆还有空间但 GC 抖动,也要检查大对象分配。
最后,GC 优化要做对比实验。改堆大小、换 GC、调 region,不要一次改一堆。每次只改一个变量,观察 P95/P99、CPU、吞吐和内存成本。
GC 日志也要保存足够周期。很多问题只在高峰、批任务或特定租户流量下出现,如果日志很快被覆盖,就无法回看。生产环境可以压缩归档 GC 日志,并在告警触发时保留现场。
gc_log_retention: keep_days: 7 archive_on_alert: true attach_to_incident: true还要注意容器内存限制。JVM 看到的可用内存、容器 limit 和实际节点压力如果不一致,GC 判断会偏。排查容器化 Java 服务时,必须同时看 JVM 参数和 cgroup 限制。
GC 参数调优也有 Trade-offs。降低暂停时间通常意味着更频繁的 GC 周期和更高的 CPU 开销;增大堆可以减少 GC 频率,但会增加单次暂停时间和内存成本。在容器化环境中,还要考虑节点上其他容器的内存压力。一个实用的做法是先确定服务的 SLO(如 P99 延迟不超过 500ms),再反过来推导可接受的 GC 暂停上限,而不是一味追求"最少 GC 次数"。对于延迟敏感服务,G1 的MaxGCPauseMillis设置要保守,避免系统为了追求低暂停而频繁并发标记,反而增加 CPU 消耗。
GC 日志分析工具的选型也值得关注。开源工具如 GCViewer、GCeasy 适合快速概览,但在处理超大日志文件(如几天的高频 GC 日志)时可能性能不足。对于关键服务,建议在构建 pipeline 里集成自动 GC 分析,每次发布后对比 GC 行为变化,而不是等线上出问题才看日志。可以设置简单的阈值告警,如"年轻代 GC 频率比上周增加 30%"或"Full GC 后老年代回收比例低于 20%",这些是内存泄漏或堆设置不合理的早期信号。
五、总结
JVM GC 日志分析要看暂停类型、暂停时间、回收效果、分配速率、GC 原因和业务延迟关联。
别只看 Full GC 次数。GC 排障要回答的是:它有没有破坏服务目标,以及为什么。