g1和zgc停顿时间应直接查看gc日志中的pause时间;g1停顿受堆大小、分配速率及老年代存活率影响大,zgc停顿基本与堆大小无关但更依赖cpu资源。

怎么看 G1 和 ZGC 的实际停顿时间
直接看 GC 日志里的 pause 时间,不是看吞吐量或平均 GC 频率。G1 的停顿受堆大小、对象分配速率、老年代存活对象比例影响很大;ZGC 的停顿基本与堆大小无关,但对 CPU 资源更敏感。
实操建议:
- 用
-Xlog:gc*:file=gc.log:time,uptime,level,tags(JDK 11+)统一开启日志,避免用旧的-XX:+PrintGCDetails - 确保测试时堆内存 ≥ 16GB(比如
-Xms32g -Xmx32g),否则 ZGC 优势不明显,G1 也压不出长停顿 - 用真实业务负载打点,别只跑
System.gc()或空循环——ZGC 对“分配风暴”容忍好,但对频繁反射/JNI 调用下的元空间压力没优化
为什么 G1 在 32GB 堆上可能卡住 200ms+
G1 的停顿由年轻代回收 + 可选的老年代并发标记触发的混合回收组成。当老年代存活对象多、跨代引用复杂时,混合回收阶段的根扫描和转移(Evacuation)会阻塞应用线程。
常见错误现象:
-
G1 Evacuation Pause (mixed)持续超 150ms,且日志里出现to-space exhausted - 设置
-XX:MaxGCPauseMillis=50后反而 GC 更频繁、停顿更抖——G1 把目标当软约束,不是硬保障 - 启用
-XX:+UseStringDeduplication后停顿变长:字符串去重在 GC 线程中同步执行,加重 STW
ZGC 的 Pause 日志里为什么总显示 1–10ms 却还是感觉卡
ZGC 的 STW 阶段确实极短,但它的并发阶段(如并发标记、并发重定位)会持续占用 CPU,如果业务线程密集计算,就会抢资源,造成毛刺感——这不是停顿,是争抢。
使用场景注意点:
- ZGC 默认启用
-XX:+UseZGC即可,但必须配-XX:+UnlockExperimentalVMOptions(JDK 11–14),JDK 15+ 才免解锁 - 不要设
-XX:SoftRefLRUPolicyMSPerMB过低:ZGC 不回收软引用,软引用堆积会拖慢并发标记 - 监控
ZGC Pauses(STW)和ZGC Cycles(整个 GC 周期)两个指标,后者若远长于前者,说明并发阶段吃 CPU
参数调优时最容易踩的坑
调参不是套公式,G1 和 ZGC 的关键参数作用机制完全不同。
典型误操作:
- 给 G1 加
-XX:G1NewSizePercent=30试图稳住年轻代——反而导致混合回收更激进,停顿方差更大 - 给 ZGC 设
-XX:ZCollectionInterval=5强制每 5 秒 GC 一次——ZGC 是自适应触发,人工干预常引发无谓的并发周期 - 混用
-XX:+UseLargePages和容器环境(如 Docker):宿主机未配置大页或 cgroup 限制下,ZGC 可能 fallback 到普通页,日志里不报错但性能掉档 - 忽略
/proc/sys/vm/swappiness:Linux 下设为 1(非 0)才能让 ZGC 的内存映射行为更稳定,否则可能触发 swap-out 导致重定位失败重试
真正难的不是选哪个收集器,是把 GC 行为和业务毛刺对齐到同一时间维度去分析——比如看到一次 8ms 的 ZGC pause,但业务请求耗时突增 200ms,那大概率不是 GC 的问题,是锁竞争或者远程调用卡住了。










