GC ROOTS的真实含义

GC ROOTS的真实含义

之前看深入理解JVM这本书,对里面的GC ROOTS的真实含义不是太清楚,网上查了一大堆资料都没有说的很清楚,下面这是从知乎大神上看到的,这里面记录一下,和大家一起学习。

含义说明

所谓“GC Roots”,或者说tracing GC的“根集合”,就是一组必须活跃的引用

例如说,这些引用可能包括:

  1. 所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
  2. VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
  3. 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还要更大一些。如果不能理解这个道理,那整个讨论也就无从谈起了。


  转载请注明: ForwardXu GC ROOTS的真实含义

 上一篇
JVM 对象查询语言(OQL) JVM 对象查询语言(OQL)
JVM 对象查询语言(OQL)本文主要翻译自JDK 1.8的JVM监控工具jhat中关于OQL的英文帮助说明。 可以在jhat 和 jvisualvm 中进行实践。 OQL(对象查询语言)OQL是用于查询Java堆的类SQL查询语言。OQL
2019-01-14
下一篇 
hbase堆外内存溢出底层定位 hbase堆外内存溢出底层定位
hbase堆外内存溢出底层定位 下文所说的 hbase client 版本,如无特指,则皆为 1.2.3。 之前项目中出现堆外内存溢出(排查过程),虽然已经解决了问题,但当时没有深究底层的原理,最近抽空从底层入手,深入研究了 hbase
2019-01-10
  目录