zgc的多重映射是将同一物理内存页同时映射到marked0、marked1和remapped三个互不重叠的虚拟地址区间,支持并发转移与读屏障自愈,全程无需stw。

ZGC 的多重映射不是给单个变量做多份地址,而是把同一块物理内存页,同时映射到三个互不重叠的虚拟地址区间:Marked0、Marked1 和 Remapped。这种设计让 GC 线程能在后台移动对象,而 Java 应用线程照常读写,全程无需 Stop-The-World。
多重映射解决什么问题
传统 GC 在对象转移时,要么暂停应用(STW),要么靠写屏障拦截所有写操作——开销大、延迟高。ZGC 换了一条路:利用操作系统 mmap 机制,让旧地址和新地址都“有效”,再靠读屏障自动跳转到最新副本。关键在于——应用看到的是虚拟地址,完全不感知物理页是否被复制或回收。
- 对象刚分配时,Marked0、Marked1、Remapped 三组地址可能都指向同一物理页
- GC 开始转移后,Marked0 仍指向旧页(内容已过期),Remapped 指向新页(含最新副本)
- 读屏障检测到指针处于 Marked0/Marked1 视图且对象已被转移,就透明地重定向到 Remapped 地址
三个虚拟视图怎么共用物理内存
操作系统允许不同虚拟地址范围映射到相同或不同物理页帧。ZGC 启动时就通过 mmap 系统调用为每块物理页建立三套映射:
- Marked0:当前活跃标记视图,也是本轮转移的源地址;指向对象当前所在的物理页
- Marked1:备用标记视图,与 Marked0 交替使用,避免跨轮次标记状态混淆;初始也映射同页,但逻辑上不参与当前访问
- Remapped:稳定视图,只在对象完成复制后激活;指向迁移后的新物理页
这三个地址空间彼此隔离,但底层可共享物理页(如新对象未转移时),也可分置(如转移完成后旧页待回收、新页已就绪)。
为什么必须依赖虚拟内存机制
硬件 MMU(内存管理单元)负责把虚拟地址翻译成物理地址。ZGC 不直接操作物理内存,而是让 OS 维护页表项(PTE),控制哪些虚拟页映射到哪块物理帧。当 GC 修改映射关系(比如把 Remapped 地址指向新页),只需更新页表,无需改写对象引用——Java 线程继续用原指针访问,MMU 自动完成地址转换。
- 染色指针(Color Pointers)把标记位存在指针低几位,导致同一对象在不同阶段“看起来”地址不同;多重映射确保这些不同虚拟地址都能落到正确物理页
- 没有虚拟内存抽象,就无法实现这种“地址变、物理不动、访问不断”的并发转移
- 该机制天然支持 TB 级堆——虚拟地址空间足够大,物理内存按需映射,不必一次性全加载
它和普通 mmap 有什么区别
普通 mmap 通常是一对一映射(一个虚拟区间 ↔ 一个物理区间)。ZGC 的特殊性在于一对多映射:同一物理页被反复 mmap 到多个虚拟地址范围,并由 GC 控制各视图的启用/禁用状态。
- 不是靠复制数据,而是靠页表重定向实现“逻辑移动”
- 不依赖 JVM 堆内结构,而是下沉到 OS 内存管理层,因此性能开销极低
- 仅限 Linux 64 位平台,因需足够大的虚拟地址空间容纳三套视图(典型配置下各占 2^42 字节)











