OopMap 和 RememberedSet

OopMap 用于枚举 GC Roots,记录栈中引用数据类型的位置。

记录栈上本地变量到堆上对象的引用关系。其作用时,垃圾收集时,收集线程会堆栈上的内存进行扫描,看看哪些位置存储了Reference类型。如果风险某个位置确实存的时Reference类型,就以为着它锁引用的对象这一次不能被回收。问题,栈上的本地变量表里面只有一部分数据时Reference类型的,那些非Reference类型的数据对我们而言毫无用途,但我们还是不得不堆整个栈全部扫描一遍,这是堆时间和资源的一种浪费。

一个很自然的想法时,能不能用空间换时间?在某个时候把栈上代表的引用的位置全部记录下来,这样到真正gc的时候就可以直接读取,而不用再一点一点的扫描了,Hotspot就是实现的。它使用一种叫做OopMak的数据结构来记录这类信息。

一个线程以为一个栈,一个栈由多个栈桢组成,一个栈桢对应一个方法,一个方法有多个安全点。GC发生时,程序首先运行到最近的一个安全点停下来,然后更新自己的OopMap,记录栈上哪些位置代表着引用。枚举根节点时,递归遍历每个栈桢的OopMap ,通过栈中记录的被引用的对象内存地址,即可找到这些对象(GC Roots)。

oopMap

  • 可以避免全栈扫描,加快枚举根节点的速度

  • 可以帮助HotSpot实现准确式GC

RememberedSet 用于可达性分析

RememberedSet 用于处理这类问题:比如说,新生代 gc (它发生得非常频繁)。一般来说, gc 过程是这样的:首先枚举根节点。根节点有可能在新生代中,也有可能在老年代中。这里由于我们只想收集新生代(换句话说,不想收集老年代),所以没有必要对位于老年代的 GC Roots 做全面的可达性分析。但问题是,确实可能存在位于老年代的某个 GC Root,它引用了新生代的某个对象,这个对象你是不能清除的。那怎么办呢?
      仍然是拿空间换时间的办法。事实上,对于位于不同年代对象之间的引用关系,虚拟机会在程序运行过程中给记录下来。对应上面所举的例子,“老年代对象引用新生代对象”这种关系,会在引用关系发生时,在新生代边上专门开辟一块空间记录下来,这就是 RememberedSet 。所以“新生代的 GC Roots ” + “ RememberedSet 存储的内容”,才是新生代收集时真正的 GC Roots 。然后就可以以此为据,在新生代上做可达性分析,进行垃圾回收。

       我们知道, G1 收集器使用的是化整为零的思想,把一块大的内存划分成很多个域( Region )。但问题是,难免有一个 Region 中的对象引用另一个 Region 中对象的情况。为了达到可以以 Region 为单位进行垃圾回收的目的, G1 收集器也使用了 RememberedSet 这种技术,在各个 Region 上记录自家的对象被外面对象引用的情况。

参考:https://www.iteye.com/blog/dsxwjhf-2201685

发布了10 篇原创文章 · 获赞 6 · 访问量 1442

猜你喜欢

转载自blog.csdn.net/yueyazhishang/article/details/105482488