JVM 之 OopMap 和RememberedSet

总体而言:

        OopMap用于GC Roots; RememberedSet用于可达性分析。

OopMap

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

          一个很自然的想法是,能不能用空间换时间,在某个时候把栈上代表引用的位置全部录下来,这样到真正gc的时候就可以直接读取,而不用再一点一点的扫描了。事实上,大部分主流的虚拟机也正是怎么做的,比如HotSpot,它使用一种叫做OopMap数据结构来记录这些信息。

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

        通过上面的解释,我们可以很清楚的看到使用OopMap可以避免全栈扫描,加快枚举根节点的速度。但这并不是它的全部用意。它的另一个更根本的作用是,可以帮助HotSpot实现准确式GC。关于准确式GC的具体内容(如:什么叫准确式 GC ?什么叫保守式 GC ?什么叫半保守式 GC ?准确式 GC 有哪些实现思路?等等),在此不一一说明,大家可以参考 找出栈上的指针/引用 这篇文章。需要说明的是,该文章的作者是 Oracle HotSpot 虚拟机团队的开发人员。 

RememberedSet

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

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

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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值