JVM-卡表

卡表

  • [一、 引言:为什么我们需要关心卡表?](#一、 引言:为什么我们需要关心卡表?)
  • [二、 背景知识:卡表面临的问题场景**](#二、 背景知识:卡表面临的问题场景**)
  • [三、 什么是卡表(Card Table)?](#三、 什么是卡表(Card Table)?)
  • [四、 卡表如何工作:写屏障(Write Barrier)](#四、 卡表如何工作:写屏障(Write Barrier))
  • [五、 卡表的应用场景](#五、 卡表的应用场景)
  • [六、 卡表的优缺点](#六、 卡表的优缺点)
  • [七、 卡表与记忆集的辨析](#七、 卡表与记忆集的辨析)
  • [八、 示例(伪代码)](#八、 示例(伪代码))
  • [九、 总结](#九、 总结)

一、 引言:为什么我们需要关心卡表?

  1. 垃圾回收的挑战: 简述自动内存管理的必要性以及垃圾回收(GC)的目标(回收内存、避免内存泄漏)。提及 GC 对应用程序性能的影响,特别是 Stop-The-World (STW) 暂停。
  2. 现代GC的趋势: 介绍为了减少 STW,现代 GC(如 CMS、G1、ZGC、Shenandoah)引入了并发(Concurrent)和分代(Generational)等关键技术。
  3. 引出问题: 指出这些高级技术带来了新的挑战:
    • 并发标记问题: GC 线程标记对象时,应用程序线程可能同时在修改对象引用,如何保证不错过存活对象?
    • 分代回收效率问题: Minor GC 只回收年轻代,但老年代对象可能引用年轻代对象,如何快速找到这些引用而不扫描整个老年代?
  4. 主角登场: 点明卡表(Card Table)是解决上述问题的核心机制之一,是理解现代 GC 内部工作原理的关键。

二、 背景知识:卡表面临的问题场景**

场景一:并发标记的"对象消失"风险

复制代码
*   用简单的例子或图示,详细解释在并发标记过程中,如果应用程序线程修改了一个已被 GC 访问过的对象(黑色)的引用,使其指向一个尚未被访问的对象(白色),并且删除了其他指向白色对象的引用,会导致白色对象被错误回收的问题。
*   强调需要一种机制来记录并发期间发生的关键引用修改。

场景二:分代垃圾回收的效率瓶颈

复制代码
*   解释分代假说(大部分对象朝生夕灭)和分代回收的基本原理(分年轻代、老年代,进行 Minor GC 和 Full GC/Major GC)。
*   说明 Minor GC 时,除了扫描 GC Roots,还必须考虑从老年代指向年轻代的引用。
*   点明如果每次 Minor GC 都扫描整个老年代来查找这些引用,将极其低效,违背了分代回收的初衷。
*   引出需要一个数据结构来"记住"哪些老年代区域可能存在指向年轻代的引用------这就是**记忆集(Remembered Set)**的概念。

三、 什么是卡表(Card Table)?

  1. 核心定义: 卡表本质上是一个字节数组(byte[]
  2. 映射关系: 它将整个堆内存 划分成固定大小的连续区域,每个区域称为一个"卡页(Card Page) "或"卡(Card) "(通常是 2 的幂次方大小,如 512 字节)。卡表中的每一个字节 都对应堆内存中的一个卡页
    • 公式:card_table_index = memory_address / CARD_PAGE_SIZE
    • 图示: 画一个堆内存条,下面对应一个卡表数组,展示其映射关系。
  3. 卡的状态: 卡表中每个字节的值代表其对应卡页的状态。最简单的实现中,有两种状态:
    • 干净(Clean): 通常用 0 表示,意味着对应的卡页"可能没有"需要关心的引用(具体含义看应用场景)。
    • 脏(Dirty): 通常用某个非 0 值(如 1 或特定标记值)表示,意味着对应的卡页"可能包含"需要关心的引用,需要 GC 在特定阶段进行检查。
  4. 类比: 使用之前提到的"仓库区域地图"的比喻,地图上的每个格子代表一个卡页,画叉表示"脏"。

四、 卡表如何工作:写屏障(Write Barrier)

  1. 触发时机: 卡表的更新依赖于写屏障(Write Barrier) 。写屏障是 JVM 在执行对象引用赋值 操作(如 object.field = reference;)时,额外执行的一小段代码。
  2. 写屏障的作用:
    • 当发生引用赋值 a.field = b 时,写屏障被触发。
    • 写屏障会计算出对象 a 所在的内存地址对应的卡页索引
    • 然后,将卡表中该索引对应的字节标记为"脏" (card_table[index] = DIRTY)。
  3. 关键特性:
    • 后写屏障(Post-Write Barrier): 通常在赋值操作之后执行。
    • 无条件标记(常见实现): 很多实现为了效率,只要发生引用写入,就标记卡为脏,而不去判断 ab 的颜色(并发标记场景)或代(分代场景)。这是一种粗粒度但高效的方式。
    • 效率: 写屏障的操作非常快,对应用程序吞吐量影响相对较小。

五、 卡表的应用场景

  • 场景一:实现分代回收的记忆集(Remembered Set)

    • 目标: 记录老年代到年轻代的引用。
    • 机制: 当写屏障检测到老年代对象 a 的字段被修改,指向任何对象 b 时(为了简化和覆盖所有情况,通常不判断b是否在年轻代),就将 a 所在的卡页标记为脏。
    • Minor GC 时: GC 只需扫描 GC Roots 和卡表中所有标记为脏的老年代卡页,查找其中的对象是否有指向年轻代的引用,而无需扫描整个老年代。
    • 卡表 = 记忆集的粗粒度实现。
  • 场景二:支持并发标记(如 CMS、G1 的增量更新)

    • 目标: 解决并发标记期间的"对象消失"问题。
    • 机制(增量更新 - Incremental Update): 当写屏障检测到引用赋值 a.field = b 时,将 a 所在的卡页标记为脏。这记录了在并发标记期间,哪些内存区域发生了写操作
    • 重新标记(Remark)阶段(STW): GC 暂停应用程序,然后只扫描那些标记为脏的卡页中的对象。检查这些对象是否有指向白色对象的引用,如果有,则从白色对象开始继续标记,确保所有存活对象都被找到。
    • 卡表 = 追踪并发修改的机制。

六、 卡表的优缺点

  • 优点:
    • 空间效率高: 相比记录精确指针,卡表只需很小的额外空间(HeapSize / CardSize)。
    • 时间效率高: 写屏障的标记操作非常快,对应用影响小。
  • 缺点:
    • 伪共享(False Sharing): 卡表的粒度是卡页。如果一个卡页内有多个对象,只要其中一个对象的引用被修改,整个卡页都会变脏。导致 GC 在处理脏卡时,可能扫描了大量实际上没有发生相关变化的对象,造成额外的开销。

七、 卡表与记忆集的辨析

  • 再次强调:记忆集 是一个逻辑概念 (记录跨区域引用的数据结构),卡表 是实现记忆集的一种具体且常用的物理方式
  • 卡表可以服务于不同的目的(跨代引用跟踪、并发修改跟踪),但其基本结构和标记机制是相似的。

八、 示例(伪代码)

  • 一个极简的写屏障伪代码,说明如何计算卡索引并标记。

    java 复制代码
    // 伪代码:对象引用赋值时的写屏障
    void assignReference(Object obj, Field field, Object newValue) {
        // 1. 执行实际的赋值
        field.set(obj, newValue);
    
        // 2. 写屏障逻辑 (Post-Write Barrier)
        long objAddress = getAddress(obj);
        int cardIndex = (int)(objAddress >> CARD_SHIFT); // CARD_SHIFT = log2(CARD_SIZE)
        if (cardTable[cardIndex] != DIRTY_VALUE) {
            cardTable[cardIndex] = DIRTY_VALUE;
            // G1等收集器可能还有额外操作,如将脏卡加入队列
        }
    }

九、 总结

  • 卡表是现代 JVM GC 中一种重要且高效的基础设施。
  • 它通过写屏障机制,以粗粒度的方式(标记卡页为脏)记录了内存区域的修改或潜在的跨区引用。
  • 它既是实现分代回收中记忆集的关键技术,也是保障 CMS、G1 等并发收集器正确性的重要机制(通过增量更新)。
  • 理解卡表有助于深入理解 GC 的性能和行为。

Happy coding!

相关推荐
jack_xu3 分钟前
经典大厂面试题——缓存穿透、缓存击穿、缓存雪崩
java·redis·后端
CHQIUU1 小时前
Java 设计模式心法之第4篇 - 单例 (Singleton) 的正确打开方式与避坑指南
java·单例模式·设计模式
碎梦归途1 小时前
23种设计模式-结构型模式之享元模式(Java版本)
java·开发语言·jvm·设计模式·享元模式
明月看潮生1 小时前
青少年编程与数学 02-018 C++数据结构与算法 06课题、树
数据结构·c++·算法·青少年编程·编程与数学
小指纹1 小时前
动态规划(一)【背包】
c++·算法·动态规划
_安晓1 小时前
数据结构 -- 图的应用(一)
数据结构·算法·图论
lozhyf2 小时前
Eureka搭建
java·spring cloud
阳洞洞2 小时前
leetcode 二分查找应用
算法·leetcode·二分查找
猎猎长风2 小时前
【数据结构和算法】1. 数据结构和算法简介、二分搜索
数据结构·算法
Pasregret2 小时前
模板方法模式:定义算法骨架的设计模式
算法·设计模式·模板方法模式