ZGC的低延迟依赖并发处理,但需权衡CPU资源:默认ConcGCThreads为CPU核数的1/4~1/2,高核数服务器宜调至32~48;Java 23新增ZGCParallelism(推荐60%~80%)实现动态线程分配;读写屏障带来持续开销,需优化引用链与缓存;分代ZGC使CPU负载更平滑,但增加记忆集维护成本。

ZGC的并发处理能力是其低延迟优势的核心,但它并非“免费午餐”——并发标记、并发重定位等阶段需要额外CPU资源支撑。这些线程与应用线程共享物理核心,若配置不当,容易引发CPU争抢、调度开销上升,甚至拖慢业务吞吐。
并发线程数与CPU核数的匹配逻辑
ZGC默认并发线程数(ConcGCThreads)通常设为 CPU 核心数的 1/4 到 1/2,这是兼顾标记速度与应用线程可用性的经验值。但这个默认值在高核数服务器(如 64 核以上)下往往偏低,导致并发标记阶段拉长,反而推高整体 GC 周期耗时。
- 例如:64 核机器上,默认 ConcGCThreads ≈ 16,但若堆中存在大量本地缓存对象(如 3GB Caffeine 缓存),标记图遍历压力大,此时可适度提升至 32~48,加速完成扫描
- 注意:线程数不是越多越好。超过合理上限后,线程上下文切换、内存带宽竞争、TLB 压力会抵消并行收益,实测中 ConcGCThreads > CPU 核数 × 75% 时,性能常出现拐点
ZGCParallelism 参数的精细化控制(Java 23 新增)
Java 23 引入 -XX:ZGCParallelism,替代了过去依赖手动调 ConcGCThreads 的粗放方式。它以百分比形式表达并行度目标,ZGC 内部自动换算为实际线程数,并协同调整标记与重定位阶段的资源分配。
- 推荐设置为 60%~80%:例如 128 核服务器,-XX:ZGCParallelism=70% 会动态启用约 89 个并发 GC 线程,且在标记高峰自动倾斜资源,避免重定位阶段空转
- 该参数对多 NUMA 节点环境更友好——ZGC 会优先在本地 NUMA 节点内调度线程,减少跨节点内存访问延迟
读屏障与写屏障带来的持续开销
ZGC 的并发安全依赖读屏障(Load Barrier)和写屏障(Write Barrier),它们在每次对象引用访问或字段更新时插入轻量检查逻辑。这部分开销虽单次微乎其微,但在高频访问场景(如缓存命中率高、POJO 字段密集读写)下会累积成可观的 CPU 占用。
- 典型表现:应用线程 CPU 使用率稳定在 70%+,但业务 QPS 未达瓶颈,火焰图中可见大量
zgc_load_barrier或zgc_store_barrier栈帧 - 缓解方式:避免无谓的对象引用穿透(如链式调用
a.getB().getC().getValue()),改用局部变量缓存中间引用;对只读缓存结构,考虑使用VarHandle或Unsafe绕过部分屏障(需严格验证安全性)
分代 ZGC 对 CPU 负担的双向影响
自 Java 17 起支持的分代模式(-XX:+ZGenerational)改变了资源消耗模型:年轻代回收更频繁但轻量,老年代并发标记仍保持全堆扫描。总体来看,它把一部分 CPU 压力从单次长周期转移到多次短周期。
- 优势:避免突发大对象晋升触发全堆标记风暴,CPU 占用曲线更平滑,适合流量峰谷明显的在线服务
- 代价:新增记忆集(Remembered Set)维护开销,每次跨代引用写入都需更新卡表,带来额外写屏障成本;需监控
ZStatistics::RememberedSet相关指标是否异常增长

















