在java中的垃圾收集器 – 设置对象为null

让我们假设,有一个Tree对象,有一个根节点TreeNode对象,每个TreeNode都有Left节点和Right节点对象(例如一个BinaryTree对象)

如果我打电话:

myTree = null; 

树中相关的TreeNode对象究竟发生了什么? 将被垃圾收集,或者我必须设置null对象树内的所有相关对象?

Java中的垃圾收集是基于“可达性”进行的。 JLS定义如下:

“可访问的对象是可以从任何活动线程进行任何潜在的持续计算中访问的任何对象。”

只要对象可以访问* ,就不符合垃圾回收的条件。

JLS将它留给Java实现来确定如何确定一个对象是否可以访问。 如果实现不能确定,则将理论上无法访问的对象视为可访问对象,而不能将其收集。 (事实上​​,JLS允许一个实现不收集任何东西,永远不会有合理的实现。)

在实践中,(保守)可达性是通过跟踪来计算的; 通过下面的类(静态)variables和线程堆栈上的本地variables来查看可以达到的内容。


以下是对您的问题意味着什么:

如果我打电话: myTree = null; 树中相关的TreeNode对象究竟发生了什么? 将被垃圾收集,或者我必须设置null对象树内的所有相关对象?

假设myTree包含myTree的最后一个可到达的引用。

  1. 什么都不会立即
  2. 如果以前只能通过根节点访问内部节点,则现在它们无法访问,并且有资格进行垃圾回收。 (在这种情况下,将null分配给内部节点的引用是不必要的。)
  3. 但是,如果内部节点可以通过其他path到达,它们大概仍然可以到达,因此不符合垃圾回收的条件。 (在这种情况下,将null分配给内部节点的引用是一个错误,您正在拆除其他可能稍后尝试使用的数据结构。

如果myTree 包含myTree最后一个可到达的引用,那么将内部引用归零是一个错误,其原因与上面3.中的相同。


那么你应该什么时候null东西来帮助垃圾收集器呢?

你需要担心的是,当可以发现某些单元格(本地,实例或类variables,或数组元素)中的引用不会再被使用,但编译器和运行时不能! 案件大致分为三类:

  1. 在类variables中的对象引用…哪些(根据定义)永远不会超出范围。
  2. 仍然在范围内的局部variables中的对象引用…但不会被使用。 例如:

      public List<Pig> pigSquadron(boolean pigsMightFly) { List<Pig> airbornePigs = new ArrayList<Pig>(); while (...) { Pig piggy = new Pig(); ... if (pigsMightFly) { airbornePigs.add(piggy); } ... } return airbornePigs.size() > 0 ? airbornePigs : null; } 

    在上面, 我们知道如果pigsMightFly为false,那么列表对象将不会被使用。 但是没有主stream的Java编译器可以预料到这一点。

  3. 实例variables或数组单元格中的数据结构不变的对象引用意味着它们不会被使用。 @ edalorzo的堆栈示例就是一个例子。

应该指出的是,编译器/运行时有时会发现范围内variables已经死亡。 例如:

 public void method(...) { Object o = ... Object p = ... while (...) { // Do things to 'o' and 'p' } // No further references to 'o' // Do lots more things to 'p' } 

一些Java编译器/运行时可能能够检测到循环结束后不需要“o”,并将variables视为死亡。


*其实我们这里所说的是强大的可达性。 当您考虑软,弱和幻影参考时,GC可达性模型更为复杂。 但是,这些与OP的用例无关。

他们将被垃圾收集,除非你有其他的参考(可能是手动)。 如果你只是参考了树,那么是的,他们将被垃圾收集。

您不能将一个对象设置为null ,只能是一个可能包含指向该对象的指针/引用的variables。 对象本身不受此影响。 但是,如果现在没有任何活动线程(即任何正在运行的方法的本地variables)的path存在于您的对象中,则在需要内存时将被垃圾收集。 这适用于任何对象,也是从原始树对象引用的对象。

请注意,对于本地variables,如果方法(或块)很快就会完成,通常不需要将它们设置为null

myTree只是一个先前指向堆中对象的引用variables。 现在你将它设置为null。 如果您没有任何其他对该对象的引用,那么该对象将有资格进行垃圾回收。

要让垃圾收集器删除对象myTree只需在将其设置为null之后调用gc()

 myTree=null; System.gc(); 

请注意,仅当没有其他引用指向该对象时才会删除该对象。

在Java中,您不需要明确地将对象设置为null以允许它们被GC'd。 当没有对象引用时,对象符合GC的要求(忽略java.lang.ref.*类)。

在没有更多引用时收集对象。

在你的情况下,由myTree (根节点)正式引用的对象直接引用的节点将被收集,依此类推。

这当然不是这样,如果你有超出树的节点的引用。 一旦那些引用超出了范围,那些将会得到GC'd(只有它们指的是任何东西)