card table 是 jvm 为优化跨代引用处理的轻量级结构,将老年代划分为512字节卡页,用byte数组标记脏页,依赖写屏障实时更新,与rset协同实现高效minor gc。

Card Table 是 JVM 为高效处理跨代引用而设计的轻量级辅助结构,核心目标是让 Minor GC 不必扫描整个老年代,只聚焦可能持有年轻代引用的内存区域。
卡页粒度固定为 512 字节
HotSpot 将老年代线性划分为连续的、等长的内存块,每一块称为一个“卡页(Card Page)”,大小严格为 512 字节(即 2⁹ 字节)。这个值是硬编码在源码中的,不可配置。一个对象只要落在某张卡页内,它的地址右移 9 位(uintptr_t addr >> 9)就能直接算出它在卡表中的索引位置。
卡表本质是一字节数组,每个元素对应一张卡页
Card Table 本身是一个 byte 类型的一维数组,长度等于老年代总大小除以 512(向上取整)。初始时所有元素为 0,表示“干净(clean)”;一旦某张卡页中出现老年代对象引用年轻代对象,对应字节就会被设为非零值(如 0xFF),标记为“脏(dirty)”。它不记录具体引用关系,只做粗粒度存在性提示。
依赖写屏障实时维护,不靠事后扫描
卡表的更新完全由写屏障(Write Barrier)驱动,发生在每次老年代对象字段赋值指向年轻代对象的瞬间:
- JVM 在 putfield / aastore 等写操作前后自动插入屏障逻辑
- 屏障计算目标对象所在卡页索引,并原子地将对应卡表字节置为 dirty
- 该过程极快,通常由一条 CPU 原子指令完成,对业务线程影响微乎其微
与 Remembered Set 协同分工,各司其职
Card Table 和 Remembered Set(RSet)不是替代关系,而是上下游协作:
- Card Table 是“线索发生器”:只告诉 GC “哪几张卡页可能有跨代引用”
- RSet 是“引用登记表”:由后台 Refinement 线程异步扫描脏卡页,识别真实跨代字段,并把引用关系登记到对应年轻代区域的 RSet 中
- Minor GC 实际使用的 GC Roots = 传统根 + 年轻代对应 RSet 中的所有源对象










