java中的垃圾收集器 - 将对象设置为null

geo*_*geo 19 java garbage-collection

让我们假设,有一个Tree对象,具有根TreeNode对象,并且每个TreeNode都有leftNode和rightNode对象(例如BinaryTree对象)

如果我打电话:

myTree = null;
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*n C 33

Java中的垃圾收集是基于"可达性"执行的.JLS将术语定义如下:

"可到达的对象是任何可以从任何活动线程继续计算中访问的对象."

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

JLS将其留给Java实现来确定如何确定对象是否可访问.如果实现不能确定,可以将理论上无法访问的对象视为可达...而不是收集它.(事实上​​,JLS允许实现不会收集任何东西!尽管如此,没有合理的实现会这样做.)

在实践中,(保守的)可达性通过追踪来计算; 通过以下从类(静态)变量开始的引用和线程堆栈上的局部变量来查看可以达到的内容.


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

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

假设它myTree包含对树根的最后剩余可到达引用.

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

如果myTree 包含对树根的最后剩余可到达引用,则归零内部引用是错误的,原因与上面的3.相同.


所以,当null的事情来帮助垃圾收集器?

您需要担心的情​​况是,可以确定某个单元格(本地,实例或类变量或数组元素)中的引用不会再次使用,但编译器和运行时不能!案件大致分为三类:

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

     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;
     }
    
    Run Code Online (Sandbox Code Playgroud)

    在上面,我们知道如果pigsMightFly为false,则不会使用列表对象.但是,预计没有主流的Java编译器可以解决这个问题.

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

应该注意的是,编译器/运行时有时会发现范围内的变量实际上已经死了.例如:

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'
}
Run Code Online (Sandbox Code Playgroud)

一些Java编译器/运行时可能能够检测到循环结束后不需要'o',并将变量视为死.


*事实上,我们在这里谈论的是强大的可达性.当您考虑软,弱和幻像参考时,GC可达性模型会更复杂.但是,这些与OP的用例无关.


Jav*_*cal 5

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

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

myTree=null;
System.gc();
Run Code Online (Sandbox Code Playgroud)

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