ZGC的STW仅几毫秒,因其仅保留Initial Mark、Final Mark和Initial Relocate三个极短暂停阶段,其余操作全并发执行;停顿时间与堆大小无关,只取决于稳定的GC Roots数量。

为什么ZGC的STW阶段只有几个毫秒
ZGC把必须停顿的操作压缩到极小范围:仅保留Initial Mark(扫描GC Roots)、Final Mark(处理并发标记期间的引用变更)和Initial Relocate(选择首批待转移页)三个阶段。其余所有动作——包括标记、转移、重映射、引用更新——全部与应用线程并发执行。这和G1在混合回收时需暂停做卡表扫描、对象复制、记忆集更新完全不同。
染色指针如何避免转移时的写冲突
传统回收器(如G1)在转移对象时,必须停掉所有线程,否则用户线程可能还在读/写旧地址,而GC线程已把对象挪走并更新了引用——这时就会出现“写入丢失”或“读取脏数据”。ZGC用Colored Pointers把对象状态(Marked0/Marked1/Remapped)直接编码进64位指针高几位,再配合Load Barrier:每次解引用前自动检查指针颜色,若指向已转移对象但未更新引用,就现场修正并重试。这个过程对应用线程是透明的,不需要全局暂停。
内存多重映射怎么支撑并发转移
ZGC用mmap将同一块物理内存同时映射为Marked0、Marked1、Remapped三个虚拟地址空间。对象创建时,ZGC就在三个视图里都分配对应虚拟地址;回收时,GC线程在Marked0视图中标记,在Remapped视图中分配新位置,然后原子切换视图。这种设计让“旧地址仍可读”和“新地址已就绪”同时成立,转移本身不依赖引用更新完成,也就无需STW来保证一致性。
ZGC停顿时间为什么几乎不随堆增长
因为ZGC所有耗时操作都和活跃对象数量无关:标记靠读屏障触发,转移按ZPage粒度分批进行(小页2MB、中页32MB、大页独占),重映射靠转发表(Forwarding Table)查表完成。真正决定STW时长的,只有GC Roots数量(比如线程栈、静态变量、JNI句柄等),这部分规模稳定,不会因堆从8GB涨到128GB而显著增加。这也是它能宣称O(1)停顿复杂度的根本原因。
容易被忽略的一点是:ZGC的低停顿高度依赖硬件支持。比如Load Barrier在x86上靠test & jmp模拟,性能损耗明显;而在AArch64或某些带硬件TLB优化的平台,屏障开销可降至纳秒级。没做架构适配就直接上线,可能看到吞吐下降15%以上,但停顿反而没明显改善。











