跳转到内容

ZGC 与 G1 怎么选:先看停顿,再看总成本

如果 GC 停顿已经影响业务延迟,可以试 ZGC;如果现有 G1 已经达标,就先保留。ZGC 把更多回收工作并发执行,降低停顿,但仍需要 CPU 和内存余量。不能只凭堆大小或机器核数决定,也不能把短 GC 停顿当成接口延迟保证。

例如,接口 P99 偶尔超标:先把慢请求与 GC 日志对齐。只有确认停顿是主要原因,换回收器才可能解决问题;数据库慢、锁等待或 CPU 限流需要分别处理。

对比项G1ZGC
主要取舍平衡吞吐、停顿和资源优先降低停顿,更多工作并发执行
回收方式标记等阶段并发,年轻代和 Mixed GC 等仍有 STW 停顿大部分工作并发,仍有短暂停顿和屏障开销
调整重点堆、停顿目标、分配与晋升情况堆余量、分配速率、CPU 是否足够让回收跟上
验收结果业务延迟和吞吐是否达标同左,还要检查分配停滞和总体资源成本

分代 ZGC 在 JDK 21 引入。低停顿是设计目标,不能据此承诺“所有负载永远亚毫秒”或“停顿消失”。原理见 JEP 439、JEP 376;调优与限制见 Oracle ZGC 文档。

版本开启分代 ZGC
JDK 21、22-XX:+UseZGC -XX:+ZGenerational
JDK 23-XX:+UseZGC,分代已是 ZGC 默认模式
JDK 24 起-XX:+UseZGC,非分代模式移除,不再沿用 ZGenerational

版本依据:JDK 23 特性列表、JDK 24 迁移说明。这里说的是 ZGC 内部模式,不能理解为 ZGC 成了整个 JVM 的默认回收器。

Terminal window
# 示例参数,16G 不是通用推荐值
-Xms16G -Xmx16G

相同的 Xms/Xmx 可减少扩缩堆的影响,但也减少释放闲置内存的空间;容器还要给非堆内存留余量。先按应用需要设堆,别直接照抄。

Terminal window
# 只有证据表明需要调整时,再比较这一参数
-XX:ConcGCThreads=2

ConcGCThreads=2 是 GC 线程数设置,不是“只占两个 CPU 核”的硬限制。降低它可能让回收跟不上分配,反而造成分配停滞。ZGC 本身会自适应调整,先看默认配置的结果。

看到的现象下一步
G1 停顿与慢请求高度重合同负载试 ZGC,比较业务 P99、CPU、吞吐和内存
CPU 配额很紧或分配速率很高检查 CPU throttling、回收速度与堆余量;不存在“低于几核禁止 ZGC”的通用线
堆很大,但 G1 已满足延迟要求继续用 G1;大堆本身不是迁移理由
批处理主要看完成时间比较总耗时与资源成本,不能预先断定某个回收器一定更快
GC 停顿很短,接口仍然慢转查线程、锁、数据库和下游调用

对照测试固定 JDK、堆、机器配额和数据负载,保留 GC 日志,并在预热后比较。只把满足业务目标且成本合适的配置带到灰度环境。