ZGC中的“着色标记(Colored Pointers)”是其实现低延迟、大堆支持的核心技术,本质是在对象指针中嵌入状态信息,通过指针“颜色”追踪对象的生命周期状态(如是否已标记、是否已移动等)。这种设计彻底改变了传统GC对“对象头”或“记忆集”的依赖,是ZGC并发能力的关键支撑。
1. 为什么能“给指针着色”?—— 64位地址空间的“空闲位”
现代64位系统中,实际可用的物理内存地址远小于64位(例如x86-64架构仅使用48位寻址),剩余的高位(如48-63位)是“空闲未使用的”。ZGC利用这些空闲位存储“标记信息”,相当于给指针“涂上不同颜色”,每种颜色代表一种对象状态。
例如:
- 某指针的二进制表示为
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000(64位),其中高16位(前16位)空闲,ZGC可在这16位中存储状态(如“已标记”“待转移”等)。
2. 指针“颜色”代表什么?—— 核心状态定义
ZGC中指针的“颜色”主要用于标记对象的3类核心状态(不同JDK版本可能微调):
- 未标记(White):对象未被GC标记,可能是垃圾;
- 已标记(Black):对象已被GC标记为“存活”;
- 待转移/已转移(Grey):对象处于“正在被移动”或“已移动到新地址”的状态(用于并发转移阶段)。
这些状态直接存储在指针的高位空闲位中,无需修改对象本身(如对象头),因此操作轻量、高效。
3. 着色标记如何支撑ZGC的并发能力?
ZGC的核心优势是“几乎全并发”(仅极短STW),而着色标记是实现这一点的关键:
(1)并发标记阶段:通过颜色快速判断对象是否已标记
传统GC(如G1)需要遍历对象图并在对象头中标记“存活状态”,过程中可能需要暂停应用或维护复杂的记忆集。
ZGC中,GC线程在并发标记时,会直接修改对象指针的“颜色”(将未标记的“白色”改为“黑色”)。应用线程访问对象时,通过读屏障(Load Barrier)检查指针颜色:若为“白色”,则辅助GC完成标记(避免遗漏),无需暂停整个应用。
(2)并发转移阶段:通过颜色实现“对象移动与地址修正”
ZGC会将存活对象从“旧Region”转移到“新Region”(压缩内存,避免碎片),这一过程完全并发。此时着色标记的作用是:
- 当对象被转移到新地址后,原指针的颜色会被标记为“已转移”,并在指针中记录新地址的偏移量;
- 应用线程访问原指针时,读屏障会检测到“已转移”颜色,自动将指针修正为新地址(透明转发),整个过程无需暂停应用,也无需维护复杂的“转发表”。
4. 着色标记的优势:为什么比传统方式更高效?
- 无对象头依赖:传统GC(如CMS、G1)依赖对象头存储标记状态,可能与锁信息等冲突(需额外处理),而着色标记直接用指针存储状态,与对象本身解耦。
- 省去记忆集/卡表:传统GC需用记忆集记录跨区域引用(如G1的Region间引用),开销大;ZGC通过指针颜色即可追踪对象状态,无需额外数据结构。
- 并发安全轻量:读屏障仅在“指针颜色异常”时触发处理(如修正转移后的地址),多数情况下无额外开销,对应用性能影响极小。
总结
ZGC的“着色标记”本质是利用64位地址空间的空闲位存储对象状态,通过指针“颜色”替代传统的“对象头标记”和“记忆集”,实现了高效的并发标记与转移。这种设计让ZGC在处理TB级大堆时仍能保持毫秒级STW,是其低延迟特性的“灵魂技术”。