对GC ROOT的理解

作者:RednaxelaFX
链接:https://www.zhihu.com/question/53613423/answer/135743258
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

所谓“GC roots”,或者说tracing GC的“根集合”,就是一组必须活跃的引用。
例如说,这些引用可能包括:
所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
JNI handles,包括global handles和local handles
(看情况)所有当前被加载的Java类
(看情况)Java类的引用类型静态变量
(看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)
(看情况)String常量池(StringTable)里的引用
注意,是一组必须活跃的引用,不是对象。

Tracing GC的根本思路就是:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。注意再注意:tracing GC的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。
GC roots这组引用是tracing GC的起点。要实现语义正确的tracing GC,就必须要能完整枚举出所有的GC roots,否则就可能会漏扫描应该存活的对象,导致GC错误回收了这些被漏扫的活对象。

这就像任何递归定义的关系一样,如果只定义了递推项而不定义初始项的话,关系就无法成立——无从开始;而如果初始项定义漏了内容的话,递推出去也会漏内容。

那么分代式GC对GC roots的定义有什么影响呢?
答案是:分代式GC是一种部分收集(partial collection)的做法。在执行部分收集时,从GC堆的非收集部分指向收集部分的引用,也必须作为GC roots的一部分。
具体到分两代的分代式GC来说,如果第0代叫做young gen,第1代叫做old gen,那么如果有minor GC / young GC只收集young gen里的垃圾,则young gen属于“收集部分”,而old gen属于“非收集部分”,那么从old gen指向young gen的引用就必须作为minor GC / young GC的GC roots的一部分。
继续具体到HotSpot VM里的分两代式GC来说,除了old gen到young gen的引用之外,有些带有弱引用语义的结构,例如说记录所有当前被加载的类的SystemDictionary、记录字符串常量引用的StringTable等,在young GC时必须要作为strong GC roots,而在收集整堆的full GC时则不会被看作strong GC roots。

换句话说,young GC比full GC的GC roots还要更大一些。如果不能理解这个道理,那整个讨论也就无从谈起了。

猜你喜欢

转载自blog.csdn.net/weixin_40391011/article/details/105056825
今日推荐