为什么在讲GC ROOT时会提OopMap存放了GC ROOT,但讲跨代引用时总是以需要遍历整个老年代来引出RemberSet?

最近在学习JVM,学习了GC ROOT的概念和GC ROOT的枚举问题。在我理解中,每次枚举GC ROOT需要从栈帧中寻找引用类型的数据,所以通过OopMap来加速。但是当在看到跨代引用时,不论是深入理解JVM还是其他博客上总是认为需要遍历整个老年代的所有对象当做GC ROOT呢?

image.png
尤其是这段话让我觉得枚举年轻代 GC ROOT 或者老年代的 GC ROOT 是要遍历一遍年轻代或老年代里的所有对象呢,GC ROOT不应该都已经在OopMap里了吗?

并且后面讲RemberSet是说,把老年代分块,然后把存在跨代引用的那一块里的对象加入到GC ROOT集合里。但是从OopMap里不应该是得到了所有的GC ROOT然后,判断某些 GC ROOT 在内存中是否在存在跨代引用的那一块里吗?

补充
深入理解JVM书中提到:
image.png

image.png

疑惑是在oopMap中已经存了需要的GC ROOT 为什么还需要去遍历老年代所有对象去寻找GC ROOT呢?是因为老年代中有跨代引用的对象并不能通过栈帧寻找到,所以没有被加入到GC ROOT集中(比如老年代的对象其实已经没有外部引用了,但还没发生full gc导致没被回收,但是他又持有年轻代对象的引用),所以需要额外的寻找吗?

阅读 3.7k
1 个回答

简单说:

OopMap 负责记录堆外堆内的引用。

RemberSet 负责记录新生代老年代的跨代引用。

GC Roots 1

比如,serial 垃圾收集器。它在进行 GC Roots 枚举时,会从 OopMap 里遍历出,被堆外直接引用的 新生代对象,放进 GC Roots 集合。

然后,通过 RemberSet 找出被 老年代 直接引用的新生代对象,也加入到 GC Roots 集合。

然后接下来,才能进行完整的 可达性分析

GC Roots 3

OopMap 只能得知蓝色框的对象,就是 foo、bar、refer 这些变量,所引用的对象。因为这些变量,要么在栈里,要么在方法区里,正是 OopMap 记录的范围。

OopMap 如何知道 refer 调用链的第二个对象呢?是不能得知的!

你可以看看,新生代 里,哪些对象是 GC Root,肯定是包括 refer 调用链的第二个对象的,也就那个被 老年代 引用的对象,即 跨代引用的对象

如果没有 RemberSet ,我们的垃圾收集器,怎么能找到被跨代引用的那个对象呢?只有整个扫描 老年代,找所有到引用 新生代 的对象,然后,把这些对象所引用的 新生代对象,放入 GC Roots 集合。

有了 RemberSet 就不用这么费劲了。

还是不明白的话,可以看看俺写的 GC Roots 枚举总结:对GC Roots 枚举的理解

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题