zgc未压缩地址,而是主动收缩至42位地址空间(0x0000000000000000–0x00003fffffffffff),腾出bit42–45共4位编码gc状态,配合保留高位全零确保染色安全与硬件兼容。

ZGC 的染色指针(Colored Pointers)不是靠“压缩地址空间”来节省内存,而是通过重定义 64 位指针中**空闲高位的语义**,把 GC 状态编码进去,同时严格限制实际使用的地址位数——这本质是**地址空间裁剪 + 元数据复用**,而非传统意义上的压缩。
为什么说 ZGC 没有压缩地址,而是主动收缩可用地址范围
ZGC 并不缩小指针宽度(仍是 64 位),但明确放弃使用高位地址区域,只在低 42 位(约 4TB 虚拟地址空间)内分配对象。这不是为了省空间,而是为高位腾出 4 个稳定空闲比特用于编码:
- Linux x86_64 实际用户态虚拟地址通常只用低 47–48 位;ZGC 进一步收窄到 42 位地址 + 4 位元数据,即有效地址范围为
0x0000000000000000 – 0x00003FFFFFFFFFFF - 这 42 位地址足以覆盖 4TB 堆;超过时(如 16TB),ZGC 启用多级页表+视图切换,仍复用同一套 4 位编码逻辑,不扩展地址位宽
- 所谓“压缩”,其实是主动让高 22 位保持全零,确保指针值高位可控、可预测,为染色提供安全前提
ZGC 指针的标准位域划分(以 Linux x86_64 为主流)
典型 64 位染色指针按功能划分为固定段,JDK 源码中直接定义为宏常量:
-
地址位(Address bits):42 位,起始于 bit 0,构成对象真实偏移 —— 对应掩码
Z_ADDRESS_MASK = (1ULL (即 <code>0x00003FFFFFFFFFFF) -
元数据位(Metadata bits):4 位,紧接地址位之后,位于 bit 42–45 —— 掩码
Z_METADATA_MASK = 0xFULL (即 <code>0x3FFFFFFFFFFF0000) - 保留/对齐位(Reserved / alignment):低 3 位(bit 0–2)固定为 0(8 字节对齐),部分实现将低 10 位整体视为对齐+颜色区,但主流仍以高位 4 位为准
- 其余高位(bit 46–63)在当前实现中保留为 0,不参与编码,避免触发内核地址校验异常
如何从染色指针中安全提取地址与状态
所有操作必须基于位运算,严禁直接强转或截断。核心是两步分离:
-
提取原始地址:清除元数据位,保留地址有效位
uintptr_t real_addr = colored_ptr & Z_ADDRESS_MASK; -
提取颜色状态:隔离元数据字段后比对预定义常量
ZPointerColor color = colored_ptr & Z_METADATA_MASK;
再判断:if (color == ZMARKED0)或if (color & ZREMAPPED) - 错误做法示例:
ptr & ~0xF(只清低 4 位)会破坏合法地址低位,导致寻址错乱;(long)ptr >> 4会丢弃地址关键位,不可用
位域设计背后的硬件与系统约束
这个划分不是任意的,而是被 x86_64 地址规范和 Linux 内存管理共同锁定:
- x86_64 要求用户空间地址高 16 位必须全 0 或全 1(符号扩展),ZGC 选 bit 42–45 是在安全区内最靠上的连续 4 位,既避开内核保护区,又留足地址余量
- Linux mmap 分配低地址段时默认满足“高 22 位为 0”,ZGC 启动时强制要求内核按此布局映射堆,否则拒绝启动
- 一旦启用 ZGC,JVM 自动禁用 CompressedOops——因为后者依赖“所有指针高 32 位为 0”,而染色指针的 bit 42–45 非零,物理上冲突,无法共存











