ChatGPT解决这个技术问题 Extra ChatGPT

根源是什么?

垃圾收集的根源是什么?

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

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

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

有人可以详细说明吗?

什么糟糕的定义:) 我将从 Garbage Collection 开始
@user177833 - 你在哪里读到这些定义?
该页面中对根的定义是:“您的程序可以直接访问的任何对象引用,而无需通过另一个对象”。这与“您编程可以访问的任何参考”大不相同。这是非常具体的,因为您的程序持有对所述托管对象的引用,并且您的程序不需要遍历堆到达根。
您需要将 JVM/CLR 可视化为管理堆的实际进程。进程知道的唯一对象是正在执行的线程堆栈帧集、已加载的类以及其他一些对象。这是 GC 根;堆中的所有其他对象都可以从此集合中访问或无法访问。

a
aib

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

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

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

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


这个答案是不正确的。 GC Roots 是根据 [Veneet's answer] 由 JVM 加载的那些类,特别是线程、系统类加载器加载的类、来自堆栈的引用、JNI 和等待完成的对象。
有关可能的根列表,请参阅 this other answer。在这个答案中,Person 不是根,它可以被一个(并且很可能不止一个)根访问。
由于该问题没有在任何地方指定 Java 或 JVM(除了标签,如果您看起来足够近,标签还包含 .NET 和 CLR)并且似乎相当通用,所以我的回答也是如此。感谢您对 Java 部分的澄清,但我看不到它如何使我的通用答案无效。
在您的具体示例中,在任何托管环境中,Person 不是 GC 根; GC 根是保存对 Person 的引用的 thing。差异是微妙的,但在这个问题的背景下很重要。尽管我的回答是针对 Java 的,但它通常对任何托管语言都是正确的。您的最后一段实际上是正确的,但与给出的示例冲突。
我仍然喜欢这个答案,因为它有助于阐明 GC“一般”是如何工作的。
L
Lawrence Dol

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's website

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


您可以考虑采购此副本/粘贴的答案:yourkit.com/docs/12/help/gc_roots.jsp,或者 yourkit 可能会考虑采购您:-)。
没有提到的是等待终结的对象,JVM 持有对它们的引用,直到终结运行。
有时引用先存储在操作数堆栈中,然后再存储到局部变量表中。 GC 是如何解决这个问题的?
V
Vineet Reynolds

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

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

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

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


我可以说根是指向活动对象的指针吗?如果没有从根到对象的路径,那么垃圾收集可以声明该对象吗?
根是活动对象。不要将指针带入其中并使自己感到困惑(GC算法使用对对象的引用数来确定可达性;通过将根视为指针来查看您在那里所做的事情)。必须使用指针/引用来确定可达性。
上面的注释应该读作“根是 JVM/CLR 已知的活动对象”。将它们视为指针的问题在于 GC 算法将更加复杂,因为任何 GC 算法都处理对象的指针/引用的数量以区分活动对象和可收集对象。一旦根是指针,所有根指针(原文如此)都必须以不同方式处理,没有明显的好处。
@VineetReynolds“GC根往往是堆栈上的操作数(因为它们当前正在被线程使用)”“堆栈上的操作数”是什么意思?
@Geek,方法的局部变量,它的参数等。
L
Lawrence Dol

IBM web site 将以下内容列为 GC 根。

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

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

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

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

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

线程 一个正在运行的线程。

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

Java local 一个局部变量。例如,输入参数,或仍在线程堆栈中的方法的本地创建对象。本机堆栈 本机代码中的输入或输出参数,例如用户定义的 JNI 代码或 JVM 内部代码。许多方法都有本机部分,作为方法参数处理的对象成为垃圾收集根。例如,用于文件、网络、I/O 或反射操作的参数。

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

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

Unreachable 无法从任何其他根访问的对象,但已被 Memory Analyzer 标记为根,以便该对象可以包含在分析中。无法访问的对象通常是垃圾收集算法优化的结果。例如,一个对象可能是垃圾收集的候选对象,但它太小以至于垃圾收集过程太昂贵。在这种情况下,该对象可能不会被垃圾回收,并且可能仍然是一个无法访问的对象。默认情况下,内存分析器解析堆转储时会排除无法访问的对象。因此,这些对象不会显示在直方图、支配树或查询结果中。您可以通过单击 File > Preferences... > IBM Diagnostic Tools for Java - Memory Analyzer 来更改此行为,然后选中 Keep unreachable objects 复选框。

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

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


i
irreputable

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

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


加载的类也是根(因为它们可能包含全局/静态变量)。
仅当加载的类变得无法访问时,才能对类进行 GC;系统加载器加载的类不能被 GC'd。