GCT列即JVM累计GC总耗时(秒),等于YGCT+FGCT之和,结合ps或jstat -t获取的运行时长,可准确计算GC占比;生产环境要求GCT/总运行时间≤1%。

直接看 jstat -gc <pid> 输出的 GCT 列,再结合 JVM 已运行时长,就能算出 GC 占比——这是最准、最轻量、生产环境唯一可依赖的方式。
怎么看 GCT 这个关键数字
GCT 是 JVM 自身统计的累计 GC 总耗时(单位:秒),它包含所有 STW 阶段的真实停顿时间,不掺水。执行命令后,它就固定在输出的第 8 列(不同 JDK 版本列序可能微调,但列名始终是 GCT):
jstat -gc 12345 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT ... ... ... ... ... ... ... ... ... ... ... ... ... ... 149 3.440 8 0.295 3.735
上面最后一列 3.735 就是当前累计 GC 耗时 3.735 秒。
- 必须搭配
-t参数才能知道“已运行多久”:jstat -gc -t 12345 1000 5,第一列会显示自 JVM 启动以来的秒数 - 如果只跑一次没带
-t,可用ps -o etime= -p 12345补充获取进程已运行时间(单位:秒) -
GCT不等于YGCT + FGCT的简单相加?不会——GCT就是它们的和,JDK 源码里明确定义为YGCT + FGCT
为什么不能只信 GC 日志里的 pause 总和
GC 日志中每行 Pause 后的时间(如 real=0.023s)确实代表单次 STW,但直接 grep "Pause" gc.log | awk '{sum += $NF} END {print sum}' 得到的总和,常比 GCT 小:
- 日志缺参数:没加
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps,部分 Minor GC 或元空间回收可能不打日志 - G1/ZGC 的并发阶段(如 concurrent-mark)不计入
Pause,但会消耗 CPU 和内存资源,GCT也不含它——这属于“隐性开销”,得另看jstat -gcoldcapacity或 JFR - 如果用了
-XX:+UseStringDeduplication,去重线程的 CPU 时间不进GCT,也不进日志Pause,但它真实拖慢吞吐
占比多少才算危险
生产 Web 服务的底线很明确:GC 耗时占比 ≤ 1%,即 GCT / 运行总秒数 ≤ 0.01。换算成监控节奏就是:
- 用
jstat -gc 12345 1000 60观察一分钟:若GCT增长超过 0.6 秒,说明已超标 - 更敏感的判断法:看
GCT每秒增长速率是否稳定 > 0.05 秒/秒(即每分钟超 3 秒)——这种“细碎卡顿”对 API 延迟伤害极大 -
FGCT / GCT ≥ 40%是个强信号:哪怕 Full GC 只发生两次,也说明老年代有隐患,不是调堆就能解决的
真正难的是把 GCT 数字和业务毛刺对应上。比如 P99 延迟突增 2 秒,jstat 显示那秒 GCT 跳了 1.8 秒,基本就锁定了根因——但前提是,你得在出问题前就开着 jstat 定期采样,而不是等告警才翻日志。


















