最近在学习JVM,学习了GC ROOT的概念和GC ROOT的枚举问题。在我理解中,每次枚举GC ROOT需要从栈帧中寻找引用类型的数据,所以通过OopMap来加速。但是当在看到跨代引用时,不论是深入理解JVM还是其他博客上总是认为需要遍历整个老年代的所有对象当做GC ROOT呢?
尤其是这段话让我觉得枚举年轻代 GC ROOT 或者老年代的 GC ROOT 是要遍历一遍年轻代或老年代里的所有对象呢,GC ROOT不应该都已经在OopMap里了吗?
并且后面讲RemberSet是说,把老年代分块,然后把存在跨代引用的那一块里的对象加入到GC ROOT集合里。但是从OopMap里不应该是得到了所有的GC ROOT然后,判断某些 GC ROOT 在内存中是否在存在跨代引用的那一块里吗?
补充:
深入理解JVM书中提到:
疑惑是在oopMap中已经存了需要的GC ROOT 为什么还需要去遍历老年代所有对象去寻找GC ROOT呢?是因为老年代中有跨代引用的对象并不能通过栈帧寻找到,所以没有被加入到GC ROOT集中(比如老年代的对象其实已经没有外部引用了,但还没发生full gc导致没被回收,但是他又持有年轻代对象的引用),所以需要额外的寻找吗?
简单说:
OopMap 负责记录
堆外
对堆内
的引用。RemberSet 负责记录
新生代
与老年代
的跨代引用。比如,
serial
垃圾收集器。它在进行 GC Roots 枚举时,会从 OopMap 里遍历出,被堆外
直接引用的新生代对象
,放进 GC Roots 集合。然后,通过
RemberSet
找出被老年代
直接引用的新生代对象
,也加入到 GC Roots 集合。然后接下来,才能进行完整的
可达性分析
。OopMap 只能得知蓝色框的对象,就是 foo、bar、refer 这些变量,所引用的对象。因为这些变量,要么在栈里,要么在方法区里,正是 OopMap 记录的范围。
OopMap 如何知道 refer 调用链的第二个对象呢?是不能得知的!
你可以看看,
新生代
里,哪些对象是 GC Root,肯定是包括 refer 调用链的第二个对象的,也就那个被老年代
引用的对象,即跨代引用的对象
。如果没有
RemberSet
,我们的垃圾收集器,怎么能找到被跨代引用的那个对象呢?只有整个扫描老年代
,找所有到引用新生代
的对象,然后,把这些对象所引用的新生代对象
,放入 GC Roots 集合。有了
RemberSet
就不用这么费劲了。还是不明白的话,可以看看俺写的 GC Roots 枚举总结:对GC Roots 枚举的理解