直接看GCT占应用总运行时间的比例,如10分钟内GCT达45秒则吞吐量为92.5%,低于健康线95%;需累计YGCT+FGCT,批处理超8%、Web API超3%即需干预。

怎么看GC是否拖垮了吞吐量
直接看 GCT(总GC时间)占应用总运行时间的比例。比如用 jstat -gc <pid> 1s</pid> 每秒打点,持续观察几分钟,若 GCT 在 10 分钟内累计达 45 秒,吞吐量就是 (600−45)/600 ≈ 92.5%,已低于健康线(95%)。这说明 GC 正在明显吃掉 CPU 时间,不是“偶尔卡一下”,而是持续性损耗。
- 别只盯着单次
YGC或FGC耗时,要算累计占比——高频小停顿比低频大停顿更伤吞吐量 -
jstat输出里YGCT+FGCT=GCT,必须加起来算,漏掉FGCT容易低估严重性 - 如果应用是批处理型(如定时导出报表),
GCT占比超 8% 就该干预;如果是 Web API,超过 3% 就值得查
怎么从GC日志里算出准确停顿时间占比
启用日志参数后,用工具或脚本解析才能拿到可靠数字。光靠肉眼看 -XX:+PrintGCDetails 输出容易漏掉并发阶段的隐式暂停(比如 G1 的 evacuation pause 后面还藏着 mixed GC 中的 STW 子阶段)。
- 必加参数:
-Xloggc:/path/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M - 别用文本编辑器硬读日志——用
GCeasy或GCViewer上传日志,它会自动聚合所有 STW 阶段,给出「Total Pause Time」和「Pause Time %」两个关键值 - 注意:JDK 9+ 推荐用
-Xlog:gc*:file=gc.log:time,uptime,level,tags替代旧参数,否则可能漏掉元空间回收、ZGC 的部分阶段
吞吐量掉得厉害,但停顿时间不长?大概率是 GC 频率太高
典型现象是 YGCT 累计很高,但每次 YGC 耗时仅 20–50ms。这说明新生代太小或对象存活率高,导致对象反复被复制、晋升加速,老年代压力暗增。
- 检查
SurvivorRatio和MaxTenuringThreshold:默认-XX:SurvivorRatio=8会让 Eden 占新生代 8/10,Survivor 各占 1/10,极易撑爆;可试调为12加大 Eden - 用
jmap -histo <pid></pid>快速看前 20 名对象实例数,若byte[]、String、HashMap$Node异常多,往往是缓存没设上限或日志打印了大对象 - 避免盲目调大
-Xmx:堆从 4g 增到 8g 后,G1 的混合回收周期变长,单次mixed GC停顿可能翻倍,反而拉低吞吐量
选错垃圾回收器,吞吐量优化就是白忙
Parallel GC 适合吞吐量优先场景,但它所有阶段都 STW;而 G1 默认目标是停顿可控,若只设 -XX:MaxGCPauseMillis=200 却不调 InitiatingHeapOccupancyPercent,它可能过早触发并发标记,徒增 CPU 开销却不换吞吐量提升。
- 后台计算任务(如 Spark Executor、Flink TaskManager):用
-XX:+UseParallelGC+-XX:GCTimeRatio=99(即允许 1% GC 时间),比 G1 更稳 - 响应敏感服务(如 Spring Cloud Gateway):用
-XX:+UseG1GC,但必须配-XX:InitiatingHeapOccupancyPercent=35(别用默认 45),并监控G1 Humongous Allocation日志行,防止大对象直接进老年代引爆 Full GC - JDK 17+ 新项目,ZGC 可以开箱即用:
-XX:+UseZGC,只要堆不超过 16TB,停顿基本稳定在 10ms 内,吞吐量损失通常
真正难的不是调参,是分清“这台机器上跑的是什么负载”——同一套参数,在 Kafka Consumer 进程里能提吞吐,在 Elasticsearch 数据节点上可能引发频繁 Concurrent Mode Failure。上线前务必用真实流量压测,而不是只看本地 ab 结果。










