根源是什么?
垃圾收集的根源是什么?
我已经把root的定义看作“你程序可以访问的任何引用”,live的定义就是一个正在使用的对象,它可以是一个局部variables,静态variables。
我有点混淆区分根和活的物体之间的差异。
什么是根path? 根和活物体如何工作?
有人可以详细说明吗?
如果将内存中的对象看作是一棵树,那么“根”就是根节点 – 每个对象都可以被程序直接访问。
Person p = new Person(); p.car = new Car(RED); p.car.engine = new Engine(); p.car.horn = new AnnoyingHorn();
有四个对象; 一个人,一辆红色的汽车,发动机和喇叭。 绘制参考图:
Person [p] | Car (red) / \ Engine AnnoyingHorn
然后你会在树的“根”处结束Person
。 它是活的,因为它被一个局部variablesp
引用,程序可以随时使用它来引用Person
对象。 这也适用于其他对象,通过p.car
, p.car.engine
等
由于Person
和所有其他recursion连接的对象都是活的,如果GC收集它们,会有麻烦。
但是,请考虑一段时间后是否会执行以下操作:
p.car = new Car(BLUE);
并重新绘制图表:
Person [p] | Car (blue) Car (red) / \ Engine AnnoyingHorn
现在Person
可以通过p
和蓝色的车通过p.car
,但是红色的汽车或其部件再也不能被访问 – 它们没有连接到活的根。 他们可以安全地收集。
所以实际上每个起点(每个局部variables,全局variables,静态variables,其他线程和栈帧中的所有内容) – 每个根 – 都是recursion地跟随所有引用,以组成所有“活动”对象的列表:正在使用和不适合删除的对象。 其他一切都是垃圾,等待收集。
GC(垃圾收集器)根目录是垃圾收集器特有的对象。 垃圾收集器收集那些不是GC根的对象,并且不能被来自GC根的引用访问。
有几种GC根源。 一个对象可以属于多于一种的根。 根类是:
- 由系统类加载器加载的类 – 类。 这样的类永远不能被卸载。 他们可以通过静态字段来保存对象。 请注意,由自定义类加载器加载的类不是根,除非相应的java.lang.Class实例碰巧是其他种类的根。
- 线程 – 实时线程
- 堆栈本地 – Java方法的局部variables或参数
- JNI Local – JNI方法的局部variables或参数
- JNI Global – 全球JNI参考
- 使用的监视器 – 用作同步监视器的对象
- 由JVM保存 – 由JVM为垃圾收集而保留的对象。 实际上,这些对象的列表取决于JVM的实现。 可能的已知情况是:系统类加载器,JVM知道的一些重要的exception类,一些预分配的exception处理对象,以及在加载类时的自定义类加载器。 不幸的是,JVM绝对不提供这些对象的额外细节。 因此,由分析师决定某个“由JVM保留”属于哪个案例。
(记入YourKit网站 )
YourKit没有提到,等待最终化的对象将被保留为根,直到GC运行finalize()
方法。 这可能会导致暂时保留较大的graphics出乎意料。 一般的经验法则是不使用终结器(但这是一个不同的问题)。
根或垃圾收集根是始终可达的对象。 如果一个对象总是可到达的,那么它不符合垃圾回收的条件。 因此根本没有资格收集。 它是确定堆中所有其他对象的可达性的初始对象集合。
从垃圾收集根可到达的堆上的其他对象被视为活动对象 ,并且不适合收集; 无法访问的对象可以被标记为回收。
我比.Net平台更了解Java,所以我只会讲一个。 在Java平台上,GC根实际上是依赖于实现的。 然而,在大多数运行时中,GC根倾向于是堆栈上的操作数(对于线程当前正在使用它们)和类(静态)成员。 可达性是从大多数JVM中的这些对象计算而来的。 在其他情况下,JNI调用所使用的本地参数和操作数将被视为根集的一部分,也被用于计算可达性。
我希望这清除任何根源(集合)和什么是活的对象的任何持续怀疑。
IBM Web站点将以下内容列为GC根。
请注意,其中一些是由内存分析器完成的人工构造,但仍然很重要的是要知道如果您正在查看堆转储。
-
系统类
一个由引导加载器或系统类加载器加载的类。 例如,此类别包括rt.jar文件(Java运行时环境的一部分)中的所有类,例如java.util。*包中的类。
-
JNI本地
本地代码中的局部variables,例如用户定义的JNI代码或JVM内部代码。
-
JNI全球
本地代码中的全局variables,例如用户定义的JNI代码或JVM内部代码。
-
线程块
从活动线程块引用的对象。
-
线
正在运行的线程。
-
繁忙的监视器
所有调用wait()或notify()方法的方法,或通过调用synchronized(Object)方法或通过input同步方法进行同步的方法。 如果方法是静态的,那么根是一个类,否则它是一个对象。
-
Java本地
一个局部variables。 例如,input参数或者仍然在线程堆栈中的方法的本地创build对象。 本地堆栈
在本机代码中input或输出参数,例如用户定义的JNI代码或JVM内部代码。 许多方法具有本地部分,而作为方法参数处理的对象成为垃圾收集根。 例如,用于文件,networking,I / O或reflection操作的参数。
-
终结
队列中的对象,正在等待终结器运行。
-
未确定
具有finalize方法的对象,但尚未完成,但尚未处于终结器队列中。
-
无法访问
从其他任何根目录无法访问的对象,但被内存分析器标记为根目录,以便可以将该对象包含在分析中。
无法访问的对象通常是垃圾回收algorithm中优化的结果。 例如,一个对象可能是垃圾收集的候选对象,但是太小以至于垃圾收集过程将会过于昂贵。 在这种情况下,该对象可能不会被垃圾收集,并可能保持为不可访问的对象。
默认情况下,当内存分析器分析堆转储时,排除不可访问的对象。 这些对象因此不显示在直方图,支配树或查询结果中。 您可以通过单击文件>首选项…> IBM诊断工具 – Java内存分析器来更改此行为,然后选中保留不可达对象checkbox。
-
Java堆栈框架
一个Java堆栈框架,其中包含本地variables。 只有在设置“首选项”以将Java堆栈帧视为对象时,才会生成此类垃圾收集根目录。 有关更多信息,请参阅Java基础:线程和线程堆栈查询。
-
未知
未知根types的对象。 某些转储(例如IBM Portable Heap Dump(.phd)文件)没有根信息。 在这种情况下,Memory Analyzerparsing器会将没有入站引用或不可访问的对象标记为未知。 此操作可确保内存分析器保留转储中的所有对象。
在Java中,我会说线程是根对象。 每个活动对象都可以追溯到活动线程。 例如,一个静态对象被一个类引用,这个类被类加载器引用,被另一个类引用,被该类的一个实例引用,被引用的Runnable引用由一个活的线程。 ( 注意,class级可以是GC,不能是根 )
我们也可以考虑所有线程的“真正的”根,但是这不在标准的Java领域。 我们不能说这是什么,以及它如何引用所有的线程。