zgc实现亚毫秒级停顿的关键在于多重映射、并发标记与染色指针协同形成的闭环机制:初始标记仅扫描roots,转移和标记全程并发执行;染色指针利用64位指针高4位作为参与地址解析的状态控制位,依赖操作系统虚拟地址多重映射支持。

ZGC 实现亚毫秒级停顿,关键不在“单点优化”,而在于多重映射、并行标记与染色指针三者协同形成的闭环机制。它不靠压缩时间,而是把原本必须 STW 的操作彻底移出暂停窗口——初始标记只扫 Roots,转移和标记全在应用线程运行时并发完成。
染色指针是多重映射的硬前提
ZGC 把 64 位指针高 4 位用作状态位(Marked0/Marked1/Remapped/Finalizable),这些位不是装饰,而是参与地址解析的控制信号。操作系统需支持虚拟地址多重映射:同一物理页可映射到多个不同虚拟地址,且高位染色后仍能被 CPU 正确寻址。x86_64 Linux 下依赖 mmap(MAP_FIXED) 和大页支持;若内核
- 堆 ≥ 8MB 才激活 ZGC,低于此值自动 fallback 到 Serial GC
- 48 位地址空间下,ZGC 实际可用寻址位为 42–44 位(取决于染色位数),对应最大堆约 4–16TB(JDK16+ 支持 16TB)
- ARM64 平台 page-shift=16(64KB 页)比 page-shift=12(4KB 页)更适配大堆,减少 TLB miss 和页表开销
并行标记不等于简单多线程
ZGC 的并发标记阶段由 GC 线程与应用线程共同推进,但不是“各干各的”。读屏障是调度中枢:当应用线程加载一个未标记对象的引用时,屏障立即触发 Marked0→Marked1 状态切换,相当于把标记动作“分摊”到每一次字段访问中。这使 GC 线程无需遍历整个对象图,只需处理未被屏障覆盖的增量部分。
- 初始标记(STW)仅遍历 Roots:栈帧局部变量、静态字段、JNI 引用等,耗时通常
- 再标记(STW)是兜底检查,超时 1ms 就退回并发标记,避免卡死
- 标记过程不修改对象头,完全绕过内存屏障和缓存失效惩罚
多重映射支撑“无感转移”
对象移动不是覆盖旧地址,而是将新副本写入另一 ZPage,再通过多重映射让旧地址仍可访问——旧虚拟地址页被重映射到新物理页,读屏障检测到 Remapped 位后自动返回新地址。这种“自愈”行为让应用线程无需同步等待,也无需写屏障更新所有引用。
- 转移阶段全程并发,GC 线程复制存活对象,应用线程继续读写
- 旧地址不立即失效,避免了引用更新风暴,尤其利于高分配率场景
- 转发表(Forwarding Table)按需构建,非全局预分配,节省元数据开销
分代模式进一步收窄停顿窗口
JDK 21 起默认启用 ZGenerational(ZGC 分代模式),年轻代使用轻量级回收策略,老年代保留原有并发能力。此时初始标记仅需扫描年轻代 Roots + 跨代引用卡表,再标记阶段也大幅缩减范围,典型停顿压至 0.2–0.5ms。
- 启用方式简洁:-XX:+UseZGC -XX:+ZGenerational
- 不再需要手动调大年轻代比例——ZGC 自动根据分配速率动态调整代边界
- 对短生命周期对象密集的服务(如 API 网关、实时风控)收益显著











