JVM GC:OopMap、CardTable、RememberedSet

[以下内容适用于 Hotsport JVM]

一句话概括:OopMap 用于枚举 GC Roots 和 准确式 GC,CardTable 和 RememberSet 用于可达性分析。

OopMap

JVM GC 判断对象是否可以回收使用可达性分析的方法,可达性分析首先需要找到 GC Roots 对象。

Java 中有几种 GC Root:
1)虚拟机栈(栈帧中的局部变量表)中引用的对象【Stack Local】

OopMap 是一个映射表,记录了栈上本地变量到堆上对象的引用关系,用来判断所有位置上的数据是不是指向堆中的引用。

垃圾收集时,收集线程会对栈上的内存进行扫描,如果发现某个位置存的是引用类型,那么引用的对象暂时不能被回收。但是,栈上的本地变量表里面只有一部分数据是引用类型的,那些非引用类型的数据不需要关心。OopMap 就是一种空间换时间的方法,存储这些引用关类型的位置,在 GC 的时候只需要读取 OopMap 上的信息。

每个线程对应着一个栈,一个栈由多个栈帧组成,每个栈帧对应一个方法,一个方法里面可能有多个安全点(Safepoint)。GC 发生时,线程运行到最近的一个安全点停下来,然后更新自己的 OopMap 。遍历每个栈帧的 OopMap,通过记录的被引用对象的内存地址,即可找到这些 GC Roots。

OopMap 可以避免全栈扫描,加快找到 GC Roots 的速度。但是它的更根本的作用是,可以帮助 HotSpot 实现准确式 GC 。关于准确式 GC 的更多内容:https://rednaxelafx.iteye.com/blog/1044951

CardTable

将老年代的空间分成大小为 512bytes 的块,有一个叫 Card Table 的数组结构存储(每个位置存的是一个byte),数组的每一位对应老年代空间中的一个块。
并发标记时,如果某个对象的引用发生了变化,Card Table 就标记该对象所在的块为 Dirty Card。

在GC时,Dirty Card 相当于 GC roots,可到达的新生代对象/老年代对象也是活的。

RememberedSet

一般来说,新生代 GC 过程是这样的:首先枚举根节点。根节点有可能在新生代中,也有可能在老年代中。这里由于我们只想收集新生代,所以没有必要对位于老年代的 GC Roots 做全面的可达性分析。但问题是,确实可能存在位于老年代的某个 GC Root,它引用了新生代的某个对象,这个对象你是不能清除的。那怎么办呢?

仍然是拿空间换时间的办法。对于位于不同年代对象之间的引用关系,虚拟机会在程序运行过程中给记录下来。对应上面所举的例子,“老年代对象引用新生代对象”这种关系,会在引用关系发生时,在新生代边上专门开辟一块空间记录下来,这就是 RememberedSet 。所以新生代的 GC Roots + RememberedSet 存储的内容,是新生代收集时真正的 GC Roots 。

G1 把一块大的内存划分成很多个域( Region )。总会存在一个 Region 中的对象引用另一个 Region 中对象的情况。为了达到可以以 Region 为单位进行垃圾回收的目的, G1 收集器也使用了 RememberedSet 这种技术,在各个 Region 上记录自身的对象被外面对象引用的情况。


References:
《深入 Java 虚拟机》
https://dsxwjhf.iteye.com/blog/2201685
https://rednaxelafx.iteye.com/blog/1044951
http://www.wannengye.com/pages/Mwh1g9FU/?from=timeline&isappinstalled=0

Tags:,

Add a Comment

电子邮件地址不会被公开。 必填项已用*标注

6 − 5 =