根源是什么?

时间:2011-06-16 01:32:36

标签: .net garbage-collection jvm clr gc-roots

垃圾收集的根源是什么?

我已经将root的定义读作“你编程可以访问的任何引用”,而live的定义是正在使用的对象,它可以是局部变量,静态变量。

我对区分根目标和活动对象之间的区别感到困惑。

root的路径是什么? root和live对象如何工作?

有人可以详细说明吗?

5 个答案:

答案 0 :(得分:85)

如果您将内存中的对象视为树,“根”将是根节点 - 您的程序可以立即访问每个对象。

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。它是实时的,因为它由局部变量p引用,程序可以随时使用它来引用Person对象。这也适用于其他对象,通过p.carp.car.engine

由于Person和递归连接的所有其他对象都是实时的,如果GC收集它们会有麻烦。

但是,请考虑一段时间后是否运行以下内容:

p.car = new Car(BLUE);

重新绘制图表:

     Person [p]
        |
     Car (blue)       Car (red)
                    /           \
                Engine    AnnoyingHorn

现在Person可以通过p和蓝色汽车通过p.car访问,但红色汽车或其部件无法再次访问 - 它们没有连接到一个活的根。他们可以安全地收集。

所以这真的是每个起点(每个局部变量,全局变量,静态,其他线程和堆栈帧中的所有内容) - 每个根 - 并递归跟随所有引用以构成所有“直播”的列表“对象:正在使用且不适合删除的对象。其他一切都是垃圾,等待收集。

答案 1 :(得分:35)

GC(垃圾收集器)根是垃圾收集器专用的对象。垃圾收集器收集那些非GC根并且无法通过GC根引用访问的对象。

有几种GC根。一个对象可以属于多种根。根类是:

  • 类 - 由系统类加载器加载的类。永远不能卸载这样的类。他们可以通过静态字段保存对象。请注意,自定义类加载器加载的类不是根,除非java.lang.Class的相应实例恰好是其他类的根。
  • 主题 - 直播主题
  • Stack Local - Java方法的本地变量或参数
  • JNI Local - JNI方法的局部变量或参数
  • JNI Global - 全球JNI参考
  • 使用的监视器 - 用作同步监视器的对象
  • 由JVM持有 - 由JVM为其目的从垃圾收集中保存的对象。实际上,此类对象的列表取决于JVM实现。可能的已知情况是:系统类加载器,JVM知道的一些重要异常类,一些用于异常处理的预分配对象,以及在加载类的过程中的自定义类加载器。不幸的是,JVM绝对没有为这些对象提供额外的细节。因此,由分析师决定某个“由JVM持有”属于哪种情况。

(归功于YourKit's website

YourKit未提及等待终结的对象将作为根保留,直到GC运行finalize()方法。这可能会导致意外地暂时保留大图。一般的经验法则是不使用终结器(但这是一个不同的问题)。

答案 2 :(得分:29)

根或垃圾收集根是始终可以访问的对象。如果始终可以访问对象,则它不符合垃圾回收的条件;因此,根本不具备收藏资格。它是初始对象集,从中确定堆上所有其他对象的可达性。

从垃圾收集根可到达的堆上的其他对象被视为活动对象,并且不适合收集;无法访问的对象可以标记为回收。

我比.Net平台更了解Java,所以我只会说一个。在Java平台上,GC根源实际上取决于实现。但是,在大多数运行时,GC根目录往往是堆栈上的操作数(因为它们当前由线程使用)和类的类(静态)成员。可达性是在大多数JVM中根据这些对象计算的。在其他情况下,JNI调用使用的本地参数和操作数将被视为根集的一部分,并且还用于计算可达性。

我希望这可以解决对root(set)和什么是活动对象的任何挥之不去的怀疑。

答案 3 :(得分:14)

IBM web site列出以下GC根。

请注意,其中一些是由内存分析器完成的人工构造,但是如果您正在查看堆转储,仍然很重要。

  • 系统类

    由引导加载程序或系统类加载器加载的类。例如,此类别包括rt.jar文件中的所有类(Java运行时环境的一部分),例如java.util。*包中的类。

  • JNI local

    本机代码中的局部变量,例如用户定义的JNI代码或JVM内部代码。

  • JNI全球

    本机代码中的全局变量,例如用户定义的JNI代码或JVM内部代码。

  • 线程块

    从活动线程块引用的对象。

  • <强>发

    正在运行的线程。

  • 忙碌的显示器

    调用wait()或notify()方法或同步的所有方法,例如通过调用synchronized(Object)方法或输入synchronized方法。如果方法是静态的,则根是一个类,否则它是一个对象。

  • Java local

    一个局部变量。例如,输入参数或仍在线程堆栈中的方法的本地创建对象。 本机堆栈

    本机代码中的输入或输出参数,例如用户定义的JNI代码或JVM内部代码。许多方法都有本机部分,作为方法参数处理的对象成为垃圾收集根。例如,用于文件,网络,I / O或反射操作的参数。

  • <强>终结

    队列中的对象,等待终结器运行。

  • <强>未封盘

    具有finalize方法但尚未最终确定且尚未在终结器队列中的对象。

  • <强>无法到达

    一个对象无法从任何其他根目录访问,但由Memory Analyzer标记为根目录,以便该对象可以包含在分析中。

    无法访问的对象通常是垃圾收集算法中的优化结果。例如,一个对象可能是垃圾收集的候选对象,但是它太小以至于垃圾收集过程太昂贵了。在这种情况下,对象可能不会被垃圾回收,并且可能仍然是无法访问的对象。

    默认情况下,当Memory Analyzer分析堆转储时,将排除无法访问的对象。因此,这些对象不会显示在直方图,支配树或查询结果中。您可以通过单击文件&gt;更改此行为偏好...&gt; IBM Diagnostic Tools for Java - Memory Analyzer,然后选中Keep unreachable objects复选框。

  • Java堆栈框架

    Java堆栈框架,用于保存局部变量。仅当您将“首选项”设置为将Java堆栈帧视为对象时,才会生成此类型的垃圾收集根。有关更多信息,请参阅Java基础知识:线程和线程堆栈查询。

  • <强>未知

    未知根类型的对象。某些转储(例如IBM Portable Heap Dump(.phd)文件)没有根信息。在这种情况下,Memory Analyzer解析器将没有入站引用或无法从任何其他根目录访问的对象标记为未知。此操作可确保Memory Analyzer保留转储中的所有对象。

答案 4 :(得分:3)

在java中我会说线程是根对象。每个活动对象都可以追溯到实时线程。例如,静态对象由类引用,该类由类加载器引用,该类加载器由另一个类引用,该类由该类的实例引用,...由Runnable引用,该引用被引用通过现场主题。 (注意,类可以是GC,它们不能是根

我们也可以考虑所有线程的“真正”根,但这不符合标准Java的范围。我们不能说它是什么,以及它如何引用所有线程。