zgc 使用着色指针(低4位编码状态)替代传统标记结构,通过读屏障在加载引用时判断并处理对象状态,实现无stw标记翻转、指针自愈与并发重定位。

ZGC 使用 Colored Pointers(着色指针) 在指针本身中直接编码对象的元信息,包括对象状态(如是否已标记、是否重定位),而不是依赖额外的元数据结构(如标记位图或对象头中的标记位)。这种设计让 ZGC 能在不访问对象本身的情况下,仅通过读取引用(即指针值)就快速判断对象状态,极大提升并发标记与重定位效率。
指针低 4 位用于编码对象状态
ZGC 将 Java 对象引用(64 位指针)的最低 4 位(bit 0–3)定义为“颜色位”(color bits),用于表示对象当前所处的 GC 阶段状态。其余高位仍保存真实内存地址(ZGC 使用染色指针 + 地址掩码技术,确保地址对齐后仍可寻址)。这 4 位共支持 16 种组合,但 ZGC 当前只使用其中 4 种核心状态:
- Marked0(0b0000):对象已被标记(在上一轮 GC 中被标记为存活)
- Marked1(0b0001):对象已被标记(在本轮 GC 中被标记为存活)
- Remapped(0b0010):对象已完成重定位,指针已更新为新地址
- Finalizable(0b0011):对象处于 finalization 队列中(等待执行 finalize())
注意:ZGC 采用双标记位(Marked0 / Marked1)实现并发标记的“标记翻转”(marking cycle flip),避免 STW 清空标记位——每次 GC 周期切换使用哪一位作为当前标记位,旧标记位自动失效,无需遍历清理。
状态变化由读屏障(Load Barrier)触发
ZGC 不在赋值器线程写入引用时检查或更新颜色,而是在读取引用时(即加载对象引用时)通过硬件辅助的读屏障(Load Barrier)拦截并处理:
- 若读到的指针是 Marked0/Marked1 状态,且当前 GC 处于标记阶段,读屏障会将其“再标记”(mark)并可能加入标记队列(若尚未入队)
- 若读到的是 Remapped 指针,说明已重定位完成,直接返回该指针(无需进一步处理)
- 若读到的是 未着色(全 0) 指针(即普通分配对象,尚未进入 GC 流程),读屏障通常不干预,直接返回
- 当对象被重定位后,旧地址处的对象头会被写入转发指针(forwarding pointer),而所有指向它的指针会在首次读取时经读屏障自动更新为新地址(即“自愈”)
地址对齐保障颜色位不干扰寻址
ZGC 要求对象按 2n 字节对齐(默认 256 字节,即低 8 位为 0),因此实际可用地址位只有高 56 位。ZGC 只使用低 4 位作颜色位,剩余 4 位(bit 4–7)保留为 0,确保地址掩码后仍能正确还原真实地址。例如:
- 原始指针值:0x0000_0000_1234_5678
- 假设该对象处于 Marked1 状态,则存储的指针为:0x0000_0000_1234_5679(+1)
- ZGC 运行时通过掩码(如 ~0xFF)提取地址部分:0x0000_0000_1234_5600,再根据对象对齐规则找到实际对象头位置
颜色位纯属元数据叠加,不影响内存布局与对象访问语义。
与传统标记方式的本质区别
不同于 CMS 或 G1 在对象头或外部位图中记录标记位,ZGC 的 Colored Pointers 实现了:
- 无共享标记结构:各线程通过本地读屏障独立判断状态,避免并发写冲突和缓存一致性开销
- 零成本读取未参与 GC 的对象:普通引用加载不触发屏障(JDK 11+ 支持硬件优化,多数情况无性能损失)
- 天然支持指针自愈:重定位后旧指针仍携带有效状态(Remapped),读屏障可立即解析出新地址
- 状态与指针强绑定:同一个对象的不同引用可以处于不同状态(如部分已更新、部分未更新),这是并发重定位安全的关键
这种设计使 ZGC 在超大堆(TB 级)下仍保持毫秒级暂停时间,核心正来自 Colored Pointers 对状态管理的极致简化与硬件友好性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











