ZGC通过读屏障+转发表+多重映射协同实现无悬挂指针:读屏障在每次字段读取时按需自愈旧引用,多重映射保障新旧地址共存与原子切换,并发重映射后台清理冷引用,全程无STW、无悬挂窗口。

ZGC通过并发重映射(Concurrent Remap)阶段与读屏障(Load Barrier)+ 转发表(Forward Table)协同机制,从根本上避免指针悬挂(dangling pointer),而不是“事后修复”悬挂问题。它的设计目标是让应用线程永远看不到已转移但未更新的旧地址。
重映射不是简单地批量更新引用
传统收集器(如G1)在转移对象后,需暂停应用线程(STW)遍历所有引用并逐一修正——这既耗时又易出错。ZGC不这么做。它把引用更新的责任“下沉”到每次实际读取对象的那一刻,并由硬件/虚拟内存机制兜底保障一致性。
读屏障拦截访问,自动完成“自愈”
当应用线程执行类似 Object o = obj.field; 这样的字段读取操作时,JVM会在字节码层面插入读屏障。该屏障会检查所加载的指针是否处于“Marked0”或“Marked1”状态(即已被标记为待转移或正在转移中):
- 若指针指向已转移对象的旧地址,屏障立即查转发表(Forward Table),获取新地址;
- 将新地址写回原引用位置(即“自愈”),再返回给应用线程;
- 后续再次访问该引用时,直接命中新地址,无需再走屏障。
这个过程对应用完全透明,且只在真正发生读取时触发,不扫描全堆。
内存多重映射确保视图切换无感
ZGC利用操作系统虚拟内存特性,将同一块物理内存同时映射为三个虚拟地址空间:
- Remapped 视图:指向转移后的新对象位置;
- Marked0 / Marked1 视图:用于标记阶段,保留旧地址语义;
- 所有映射指向同一物理页,但CPU根据当前使用的虚拟地址自动路由。
当对象完成转移,ZGC只需原子切换线程使用的页表项(如TLB刷新),就能让后续新分配或新访问自然落在Remapped视图下。旧引用即使未被读屏障更新,只要不再被访问,就不会造成悬挂;一旦被访问,屏障立刻纠正。
并发重映射阶段清理残留旧引用
虽然读屏障能实时“自愈”,但仍有部分引用可能长期未被读取(如静态字段、长生命周期容器中的引用)。ZGC安排一个并发重映射阶段,在后台遍历这些“冷引用”,主动更新为新地址。该阶段:
- 不阻塞应用线程;
- 可延迟执行,甚至推迟到下次GC周期;
- 仅处理尚未被读屏障触达的引用,开销远低于全堆扫描。
因此,ZGC没有传统意义上的“指针悬挂窗口期”。从对象开始转移那一刻起,任何对该对象的合法访问都会被读屏障拦截并导向正确位置——悬挂问题在架构层面被消解,而非靠后期修补。

















