zgc并发重映射通过染色指针、读屏障和多重映射三者协同实现:指针高数位编码remapped等状态,读取时由读屏障按需查表并自愈更新地址,同时借助多视图内存映射使新旧地址共存,修复过程分散于多次gc周期,无需stw。

ZGC 的并发重映射不依赖“暂停线程来统一修复指针”,而是让变量(即对象引用)在被读取的瞬间,由读屏障自动完成指向新地址的跳转——整个过程对应用代码完全透明,也无需额外同步。
指针自带状态,变量访问即触发修复
ZGC 把标记与重定位状态直接编码进 64 位指针高几位(如 mark0、mark1、remap 位),这意味着每个变量(引用)本身已携带“它当前指向的对象是否已被移动”的信息。当某行 Java 代码执行 obj.field 时,JVM 插入的读屏障会检查该指针的 remap 位:
- 若 remap 位未置位,说明对象还在原位置,直接返回原地址
- 若 remap 位已置位,说明重定位已完成,但该引用尚未更新;读屏障立即查重映射表(forwarding table),获取新地址,并将该指针“就地”更新为新地址(即自愈)
- 更新后的指针下次再读取时,remap 位+新地址已就绪,跳过查表,开销趋近于零
三视图内存映射,让新旧地址共存不冲突
ZGC 将同一物理内存页通过 mmap 映射出 Marked0、Marked1 和 Remapped 三个虚拟地址空间。变量在代码中持有的始终是某个虚拟地址(比如原 Marked0 视图下的地址),而底层页表可动态调整其映射关系:
- 重定位开始前:所有变量指向 Marked0 视图,该视图映射到物理页 A
- 重定位进行中:对象被复制到新页 B,Remapped 视图也映射到页 B;此时 Marked0 视图仍映射页 A,保证旧引用仍可安全读取(不会崩溃)
- 原子切换:仅需修改 TLB 或页表项,将 Marked0/Marked1 视图逐步重定向至 Remapped 语义(实际是切换元数据,非逐个改指针)
变量本身不需要感知这个切换——它继续用原来的虚拟地址访问,而硬件/OS 层面已悄然把请求路由到新物理页。
重映射工作被摊平到多次 GC 周期中
ZGC 不强制在单次 GC 中完成全部引用修复。它利用“指针自愈”特性,把并发重映射任务延迟并融合到下一轮并发标记中:
- 本轮 GC 完成重定位后,只确保新分配对象和活跃引用逐步进入 Remapped 视图
- 残留的旧引用(如静态字段、栈中临时变量)不主动扫描更新,而是等下次读取时由读屏障顺手修正
- 这样避免了遍历整个堆找引用的开销,也消除了因集中更新引发的瞬时延迟尖峰
变量在 ZGC 里不是被“批量重写”的目标,而是被设计成“按需响应”的参与者。它的无感知,来自染色指针的状态自描述、多视图的地址解耦,以及读屏障的轻量拦截——三者协同,把一次潜在的全局停顿,拆解成亿次微秒级的自然修复。










