根源是什么?

Dar*_*der 62 .net clr garbage-collection jvm gc-roots

垃圾收集的根源是什么?

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

我对区分root和live对象之间的区别感到困惑.

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

有人可以详细说明吗?

aib*_*aib 85

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

Person p = new Person();
p.car = new Car(RED);
p.car.engine = new Engine();
p.car.horn = new AnnoyingHorn();
Run Code Online (Sandbox Code Playgroud)

有四个对象; 一个人,一辆红色汽车,它的发动机和喇叭.绘制参考图:

     Person [p]
        |
     Car (red)
   /           \
Engine    AnnoyingHorn
Run Code Online (Sandbox Code Playgroud)

你将最终Person得到树的"根".它是实时的,因为它由局部变量引用,p程序可以随时使用它来引用该Person对象.这也无二的其他对象,通过p.car,p.car.engine等等.

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

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

p.car = new Car(BLUE);
Run Code Online (Sandbox Code Playgroud)

并重绘图表:

     Person [p]
        |
     Car (blue)       Car (red)
                    /           \
                Engine    AnnoyingHorn
Run Code Online (Sandbox Code Playgroud)

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

因此,实际上需要考虑每个起始点(每个局部变量,全局变量,静态,其他线程和堆栈帧中的所有内容) - 每个根 - 并递归地跟随所有引用以构成所有"实时"对象的列表:正在使用且不适合删除的对象.其他一切都是垃圾,等待收集.

  • 这个答案是不正确的.GC Roots是由JVM根据[Veneet的答案]加载的类,特别是线程,由系统类加载器加载的类,来自堆栈的引用,JNI和等待完成的对象. (7认同)
  • 有关可能的根列表,请参阅[此其他答案](http://stackoverflow.com/a/28057186/8946).在这个答案中,"Person"不是根,它可以被一个(很可能是多个)根访问. (3认同)
  • 由于问题没有在任何地方指定Java或JVM(除了标签,如果你看得太近也包含.NET和CLR)并且看起来相当通用,我的答案也是如此.感谢Java部分的澄清,但我没看到它如何使我的通用答案无效. (2认同)
  • 在您的特定示例中,在任何托管环境中,"Person"不是*GC根; GC根是*thing*,它包含对`Person`的引用.差异很微妙,但在这个问题的背景下很重要.虽然我的答案是针对Java的,但对于任何托管语言来说都是正确的.你的最后一段实际上是正确的,但与给出的例子有冲突. (2认同)
  • 我仍然喜欢这个答案,因为它有助于阐明 GC“一般”是如何工作的。 (2认同)

小智 35

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

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

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

(信用到YourKit的网站)

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

  • 您可以考虑采购此复制/粘贴的答案:http://www.yourkit.com/docs/12/help/gc_roots.jsp,或者您的套件可能会考虑采购您:-). (13认同)

Vin*_*lds 29

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

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

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

我希望这清除了对什么是根(集)和什么是活对象的任何挥之不去的怀疑.

  • Root是活动对象.不要将指针引入此并使自己感到困惑(GC算法使用对象的引用数来确定可达性;通过将根视为指针来查看您在那里做了什么).必须使用指针/参考来确定可达性. (3认同)

Law*_*Dol 14

IBM的网站列出以下为GC根.

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

  • 系统类

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

  • JNI当地人

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

  • JNI全球

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

  • 线程块

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

  • 线

    正在运行的线程.

  • 忙碌的监视器

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

  • Java本地

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

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

  • 终结

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

  • 未确定

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

  • 无法访问

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

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

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

  • Java堆栈框架

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

  • 未知

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