69

垃圾收集的根源是什么?

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

我对区分根对象和活动对象之间的区别有点困惑。

什么是root路径?根对象和活动对象如何工作?

有人可以详细说明吗?

4

5 回答 5

91

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

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.car,p.car.engine等。

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

但是,考虑一下,如果在一段时间后运行以下命令:

p.car = new Car(BLUE);

并重绘图形:

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

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

因此,这实际上是获取每个起点(每个局部变量、全局变量、静态变量、其他线程和堆栈帧中的所有内容)——每个根节点——并递归地跟踪所有引用以组成所有“活动”对象的列表的问题:正在使用且不适合删除的对象。其他都是垃圾,等待收集。

于 2011-06-16T02:16:56.927 回答
39

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

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

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

(归功于YourKit 的网站

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

于 2013-03-15T07:31:45.307 回答
31

根或垃圾收集根是始终可访问的对象。如果一个对象总是可达的,那么它就没有资格进行垃圾回收;因此,根始终没有资格收集。它是确定堆上所有其他对象的可达性的初始对象集。

从垃圾收集根可到达的堆上的其他对象被认为是活动对象,并且没有资格被收集;无法访问的对象可以标记为回收。

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

我希望这可以消除关于什么是根(集合)和什么是活动对象的任何挥之不去的疑问。

于 2011-06-16T01:53:48.247 回答
18

IBM 网站将以下内容列为 GC 根。

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

  • 系统类

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

  • JNI 本地

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

  • JNI 全球

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

  • 线程块

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

  • 线

    一个正在运行的线程。

  • 忙碌的监视器

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

  • Java 本地

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

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

  • 终结器

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

  • 未定案

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

  • 无法到达

    无法从任何其他根访问的对象,但内存分析器将其标记为根,以便可以将对象包含在分析中。

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

    默认情况下,内存分析器解析堆转储时会排除无法访问的对象。因此,这些对象不会显示在直方图、支配树或查询结果中。您可以通过单击 File > Preferences... > IBM Diagnostic Tools for Java - Memory Analyzer 来更改此行为,然后选中 Keep unreachable objects 复选框。

  • Java 栈帧

    Java 堆栈帧,其中包含局部变量。仅当您将 Preferences 设置为将 Java 堆栈帧视为对象时,才会生成这种类型的垃圾收集根。有关更多信息,请参阅 Java 基础知识:线程和线程堆栈查询。

  • 未知

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

于 2015-01-20T23:35:48.710 回答
3

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

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

于 2011-06-16T02:03:52.920 回答