zgc并发重分配阶段通过转发表、读屏障和染色指针协同实现按需自愈:读屏障拦截首次访问,查转发表转发至新地址并原地更新引用,后续访问直接命中;转发表仅对重分配集region维护,待下轮gc确认无旧引用后才释放。

ZGC 的并发重分配阶段,核心目标是把重分配集(Relocation Set)中存活的对象复制到新的 Region,同时保证应用线程访问对象时始终拿到正确地址、不中断、不卡顿。它不靠全局 STW 来统一修正所有引用,而是靠“转发表 + 读屏障 + 染色指针”三者协同实现按需自愈(Self-Healing)。
转发表的作用不是批量更新,而是按需转发
转发表(Forward Table)是一个 Region 级的哈希表或数组结构,每个条目记录:
- 旧对象起始地址 → 新对象起始地址
- 仅对重分配集中的 Region 维护,非重分配集对象不涉及
它不主动扫描堆去改引用,而是等待应用线程第一次访问旧对象时被读屏障拦截,再触发转发逻辑。
读屏障拦截访问,触发自愈流程
当用户线程执行 obj.field 或 load 操作时,JVM 在内存加载指令前插入读屏障(Load Barrier)。该屏障会:
- 检查目标对象指针是否为染色指针(即高几位含 Marked0/Marked1 标志)
- 若该指针指向重分配集中的旧对象(即指针已标记但尚未重映射),则查转发表
- 找到新地址后:
- 立即将本次访问重定向到新对象
- 同步把当前引用字段(如
obj.field所在位置)的值原地更新为新地址 - 返回新对象内容
这个“查表→转发→写回”的过程只发生第一次访问时,后续再读同一字段,指针已是新地址,直接命中,无额外开销。
为什么不需要立刻更新全部引用?
因为 ZGC 的染色指针自带元信息:
- 指针本身可标识对象是否处于重分配中(Marked 0/1)
- 即使大量旧引用还散落在堆里、栈里、寄存器中,只要没被读取,就无需处理
- 只有真正“用到”时才修复,把工作摊到运行时,避免集中停顿
例如:
- 对象 A 引用对象 B,B 正在重分配
- 线程 1 第一次读
A.b→ 触发读屏障 → 查转发表 → 得到 B' 地址 → 把A.b改成 B' → 返回 B' - 线程 2 后续读
A.b→ 指针已是 B' → 直接访问,零开销
转发表何时可以释放?
转发表不能随 Region 释放立即清除,必须等到:
- 所有潜在旧引用都完成自愈(即所有可能访问过旧对象的线程都至少触发过一次读屏障)
- ZGC 将这项清理工作延迟到下一轮并发标记阶段一并完成(称为“并发重映射”),避免额外遍历堆
所以,一个 Region 的对象复制完后可立即复用,但它的转发表仍保留,直到下轮 GC 显式确认无活跃旧引用为止。
本质上,ZGC 把“引用更新”从“同步强一致性操作”降级为“最终一致性 + 惰性修复”,用硬件友好的染色指针和轻量读屏障支撑起毫秒级停顿。










