ZGC 与 G1 怎么选:先看停顿,再看总成本
如果 GC 停顿已经影响业务延迟,可以试 ZGC;如果现有 G1 已经达标,就先保留。ZGC 把更多回收工作并发执行,降低停顿,但仍需要 CPU 和内存余量。不能只凭堆大小或机器核数决定,也不能把短 GC 停顿当成接口延迟保证。
例如,接口 P99 偶尔超标:先把慢请求与 GC 日志对齐。只有确认停顿是主要原因,换回收器才可能解决问题;数据库慢、锁等待或 CPU 限流需要分别处理。
| 对比项 | G1 | ZGC |
|---|---|---|
| 主要取舍 | 平衡吞吐、停顿和资源 | 优先降低停顿,更多工作并发执行 |
| 回收方式 | 标记等阶段并发,年轻代和 Mixed GC 等仍有 STW 停顿 | 大部分工作并发,仍有短暂停顿和屏障开销 |
| 调整重点 | 堆、停顿目标、分配与晋升情况 | 堆余量、分配速率、CPU 是否足够让回收跟上 |
| 验收结果 | 业务延迟和吞吐是否达标 | 同左,还要检查分配停滞和总体资源成本 |
分代 ZGC 在 JDK 21 引入。低停顿是设计目标,不能据此承诺“所有负载永远亚毫秒”或“停顿消失”。原理见 JEP 439、JEP 376;调优与限制见 Oracle ZGC 文档。
参数先对齐 JDK 版本
Section titled “参数先对齐 JDK 版本”| 版本 | 开启分代 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 的默认回收器。
# 示例参数,16G 不是通用推荐值-Xms16G -Xmx16G相同的 Xms/Xmx 可减少扩缩堆的影响,但也减少释放闲置内存的空间;容器还要给非堆内存留余量。先按应用需要设堆,别直接照抄。
# 只有证据表明需要调整时,再比较这一参数-XX:ConcGCThreads=2ConcGCThreads=2 是 GC 线程数设置,不是“只占两个 CPU 核”的硬限制。降低它可能让回收跟不上分配,反而造成分配停滞。ZGC 本身会自适应调整,先看默认配置的结果。
一张表完成选型
Section titled “一张表完成选型”| 看到的现象 | 下一步 |
|---|---|
| G1 停顿与慢请求高度重合 | 同负载试 ZGC,比较业务 P99、CPU、吞吐和内存 |
| CPU 配额很紧或分配速率很高 | 检查 CPU throttling、回收速度与堆余量;不存在“低于几核禁止 ZGC”的通用线 |
| 堆很大,但 G1 已满足延迟要求 | 继续用 G1;大堆本身不是迁移理由 |
| 批处理主要看完成时间 | 比较总耗时与资源成本,不能预先断定某个回收器一定更快 |
| GC 停顿很短,接口仍然慢 | 转查线程、锁、数据库和下游调用 |
对照测试固定 JDK、堆、机器配额和数据负载,保留 GC 日志,并在预热后比较。只把满足业务目标且成本合适的配置带到灰度环境。