转自 http://09itblog.site/?p=1093
JVM CardTable 和 RememberedSet
CardTable 和 RememberSet 用于可达性分析
Card Table
官方解释:A kind of remembered set that records where oops have changed in a generation.
将老年代的空间(在 G1 中是每个 Region)分成大小相同的块(一般为 128~512bytes
),叫做卡片(Card),即堆内存最小可用粒度。
Card Table 是一种数组结构存储,每个位置存的是一个 byte,代表一个 Card。一个 byte,有八位,通过约定每一位的含义就可区分不同的使用场景。
Remembered Set
官方解释:A data structure that records pointers between generations.
G1 GC
在 G1 GC 的内存结构中,每个 Region 都会由一个 RSet 维护并跟踪其他 Region 对本 Region 的引用(points-in 结构
),也就是存活对象。当 Region 中对象被移动时 RSet 也会更新引用。
当 GC 发生时,通过 RSet 找到引用当前 Region 的 Old Regions 进行扫描,避免了扫描全部的 Old Regions,提高扫描效率。因为每次 GC 都会扫描所有的年轻代的 Regions,所以查找 RSet 时只需要找到 Old Region 对当前 Region 的引用。
RSet 优化
当 Region 被引用较多的情况,RSet 占用空间会上升,因此对 RSet 的记录划分了三种存储粒度:
- 稀疏表(Sparse):直接通过哈希表来存储,key 是 region index,value 是 card 数组(记录 card index)
- 细粒度(Fine):当一个 region 的 card 数量超过阈值时,退化为一个 bitmap,每一位对应一个 card(index)
- 粗粒度(Coarse):当被引用的 region 数量超过阈值时,退化为只记录 regin 引用情况,由 bitmap 存储,每一位对应一个 region(index)
struct g1_rset {
hash_map<region_id, card_list> sparse;
hash_map<region_id, bitmap<MAX_CARD>> fine_grained;
bitmap<MAX_REGION> coarse;
};
CMS GC
Card Table 在 CMS GC 中也有使用到,有一块区域用来记录老年代中的每个 Card 指向新生代的引用(points-out
结构),在进行 YGC 时,这一块区域的对象作为 GC roots,而不需要扫描整个老年代(YGC 时需要知道哪些新生代对象是被老年代引用的)。
并发标记时,如果某个对象的引用发生了变化,标记该对象所在的 Card 为 Dirty Card(通过 write-barrier)。在重新标记时,只需要重新扫描 Dirty Cards 即可,同时清除 Dirty 标记。