g1收集器不使用传统标记位图,而是采用三色标记+satb快照+remembered set协同机制,结合卡表与写屏障实现高效并发标记;其标记信息分散存储于对象头或region级临时位图中,并非全局静态结构。

G1收集器并不使用传统意义上的“标记位图”(bit map)来记录对象状态,它实际采用的是三色标记算法 + SATB 快照 + Remembered Set(记忆集)协同机制,配合卡表(Card Table)和写屏障(Write Barrier)实现高效、并发的存活对象追踪。所谓“标记位图”,在 G1 中并不存在独立的全局位图结构;它的标记信息是分散存储、按需维护的。
G1 中的对象状态记录方式
-
不靠统一位图,靠颜色状态 + 元数据区域
G1 借鉴三色标记思想,但不为每个对象分配一个显式 bit 位存于位图中。而是:
- 在并发标记阶段,对象的“颜色”隐含在对象头(mark word)或专门的标记结构中(如
markOop或 G1 自定义的 bitmap 区域); - 更关键的是:G1 将标记过程与 Region 粒度绑定,只在需要时(如并发标记期间)为待扫描的 Region 分配临时标记结构(如
NextMarkBitMap),用于记录该 Region 内哪些对象已被标记为存活; - 这个 bitmap 是按 Region 分配、动态创建、仅在标记周期内有效的,并非堆全局静态位图。
- 在并发标记阶段,对象的“颜色”隐含在对象头(mark word)或专门的标记结构中(如
-
SATB 保证标记一致性,避免漏标
G1 使用 Snapshot-At-The-Beginning(SATB) 模型进行并发标记:
- 初始标记(Initial Mark)阶段 STW,记录所有 GC Roots 直接可达对象为灰色;
- 随后应用线程继续运行,但只要发生“老年代引用新对象”的写操作,写屏障会把被修改的旧引用(即将被覆盖的引用)推入 SATB 日志缓冲区;
- 并发标记线程随后消费这些日志,将旧引用指向的对象重新标记为灰色(即使它本已变成白色),防止因引用丢失导致的漏标;
- 这样就不依赖实时扫描整个堆,也不需要冻结所有写操作。
-
跨代引用靠卡表 + 记忆集,而非位图索引
当老年代对象引用年轻代对象时,G1 不靠位图查“谁引用了我”,而是:
- JVM 写屏障捕获跨代写操作(如
obj.field = youngObj); - 将对应内存块(Card,通常 512B)在所属 Region 的卡表中标记为“脏”(dirty card);
- Refinement 线程异步扫描脏卡,构建该 Region 的 Remembered Set(RSet);
- RSet 存储的是“哪些其他 Region 的哪些 Card 包含指向本 Region 的引用”,本质是哈希表或稀疏数组,不是位图。
- JVM 写屏障捕获跨代写操作(如
-
对象是否存活,最终由标记阶段+转移阶段共同确认
- Young GC 时:仅扫描 Eden/Survivor + RSet 中的跨代引用,快速确定存活对象,复制迁移;
- Mixed GC 时:基于并发标记结果(即各 Region 的存活率统计),选择垃圾最多的 Region 回收;
- 对象是否被回收,取决于它是否在当前回收集合(Collection Set, CSet)中,且未被任何根或 RSet 引用链到达——这个判断是运行时动态完成的,不依赖静态位图。
简言之:
G1 不维护一个全局、持久、按对象粒度的标记位图;它用轻量级的 Region 级临时 bitmap 辅助并发标记,用 SATB 日志保障准确性,用卡表+RSet 解决跨代引用,用对象头/元数据承载颜色状态。整套机制目标明确——降低 STW 时间、支持预测停顿、避免碎片、适配大堆,而不是追求位图式的直观标记。











