zgc的stw敏感度显著低于g1,核心差异在于停顿次数少、单次时长极短(1–10ms)、触发逻辑自适应;其并发标记阶段仅需两次极短stw(初始标记和最终标记),其余阶段完全并发。

ZGC 的 STW 敏感度显著低于 G1,核心差异不在“是否停顿”,而在于停顿的次数、时长和触发逻辑——尤其在并发标记阶段,ZGC 仅需两次极短暂停(
初始与最终标记阶段的停顿特征
G1 的初始标记(Initial Mark)和最终标记(Final Mark)均为 STW 阶段。其中最终标记需修正并发标记期间产生的引用变更,实际耗时取决于灰色对象数量和 Remembered Set 扫描开销。车载实测中,RegionSize 配置不当导致 RSet 膨胀,Final Mark 停顿从 20ms 激增至 180ms。
ZGC 的对应阶段称为 Pause Mark Start 和 Pause Mark End,均控制在 1ms 内。它不依赖全局 RSet,而是靠读屏障在应用线程读取对象时“即时修补”漏标,避免集中扫描开销。
- G1 最终标记需遍历所有待处理的灰色对象 + 扫描全部 Region 的 RSet
- ZGC 标记启动后,绝大多数工作由并发线程完成,STW 仅用于原子切换标记视图
- JDK26 中 G1 的“增量重新标记”缓解了 Final Mark 压力,但仍未消除其 STW 属性
并发标记过程中的用户线程干扰
G1 并发标记虽与用户线程并行,但存在隐式竞争:标记线程需访问对象头、更新 RSet、处理跨 Region 引用;当大量小对象分散在多个 Region 时(如车载 AudioZone 实例),RSet 更新频繁,引发缓存失效和写屏障开销,间接拖慢应用线程吞吐。
ZGC 的并发标记完全解耦:标记状态存在指针元位中,读屏障只在真正读取未标记对象时才介入,且多数情况下无操作。这意味着即使堆中活跃对象达数百万,只要引用关系稳定,用户线程几乎感知不到 GC 行为。
- G1 的写屏障(Write Barrier)每次修改引用都需记录卡表,产生固定开销
- ZGC 的读屏障(Read Barrier)是惰性的,仅在“首次读取”未标记对象时触发标记或重映射
- 高并发场景下,ZGC 的 CPU 占用更平稳,G1 易出现周期性毛刺
混合回收与重定位对响应延迟的影响
G1 的 Mixed GC 包含 Evacuation Pause,需暂停线程复制存活对象、更新引用、清理 Region。该停顿时间与复制数据量正相关,在车载等低内存嵌入式环境,ConcGCThresholdPercent 设置过低会频繁触发 Mixed GC,造成响应延迟超 200ms。
ZGC 的重定位(Relocation)全程并发,仅在 Pause Relocate Start 阶段做一次亚毫秒级原子切换。对象迁移通过内存多重映射实现:Marked0/Marked1/Remapped 三个虚拟视图指向同一物理页,应用线程始终通过当前有效视图访问,无需批量更新引用。
- G1 Evacuation 中引用更新需遍历所有根和 RSet,无法规避 STW
- ZGC 重定位后,旧地址仍可通过读屏障自动跳转到新地址,用户线程无感知
- JDK21 后分代 ZGC 进一步将年轻代回收也纳入并发流程,减少代际晋升压力
适用场景的敏感度分界点
当应用要求端到端 P99 延迟
关键判断依据不是堆大小,而是对象生命周期分布与引用变更频率:短生命周期+强局部性 → G1 更高效;长生命周期+跨代弱引用+高读取密度 → ZGC 优势放大。
- 大堆(>32GB)且延迟敏感:ZGC 几乎是唯一选择
- 中小堆(4–16GB)+ 高并发读多写少:ZGC 响应更平滑
- 内存受限(











