为什么System.gc()只回收此代码中的一个对象?

时间:2013-09-23 19:17:53

标签: java garbage-collection

在下面的代码中,在运行时只有一个对象从堆内存中清除(通过调用System.gc())。为什么只有一个物体被回收?

 class A{
    A a;
    public A(A a){
      this.a=a;
    }
    public static void main(String args[]){
      A a1=null;
      A a2=new A(new A(null));
      A a3=new A(a2);
      a1=a3;
      a1.a=new A(null);
      a2.a=null;  //Line12
      System.gc();
    }
  }

3 个答案:

答案 0 :(得分:3)

我认为尝试使用Java确定GC收集时间是非常可怕的,但无论如何......

.. Java垃圾收集工作在object reachability。如果对象是强可访问的 - 也就是说,如果可以通过可达性或已知根(包括局部变量)的任何路径访问它,那么就不能回收 1

考虑这个细分,其中W,X,Y,Z代表A的不同实例。我用来显示任何特定A的语法是instance {a-> instance},其中a指的是到成员变量。请记住,每个new创建一个不同的实例,并且对象值的分配创建新对象(因此相同的Y对象 - 现在由a1和a3共享 - 在a1.a=new A(null)分配期间被修改。

  A a1=null;                // a1 = null
  A a2=new A(new A(null));  // a2 = W {a-> X}
  A a3=new A(a2);           // a3 = Y {a-> W}    
  a1=a3;                    // a1 = a3 = Y {a-> W}
  a1.a=new A(null);         // a1 = a3 = Y {a-> Z}
  a2.a=null;                // a2 = W {a-> null}
  System.gc();

  // Then:
  // a1 = a3 = Y {a-> Z}    (Expanding: Y {a-> Z {a-> null}})
  // a2 = W {a-> null}

所以最后变量a1和a3--它们是可达性根 - 是“引用”Y {a->Z}而a2是“引用”W {a->null}。这意味着W,Y和Z都仍然可以很强地到达(Z可以通过Y强烈到达),并且在Java-land 2 中不被认为有资格进行回收。

System.gc()行无法再访问X.但是,这意味着X将被垃圾收集,即使使用显式GC调用 - 它只意味着X在未来的某个时间点被符合条件被回收

当然,一旦函数结束,所有局部变量都不再是可达性根,并且没有任何A对象可以强烈到达,使它们都符合条件:)


1 使用对象可达性来讨论GC即使不使用传统的Mark&基于扫描的方法(通常在JVM中找到),因为相同的规则适用于任何正常运行的GC系统:如果有可能访问 的对象,则不能被收回。反过来说:如果一个对象无法访问,那么它应该被回收。

2 确定可达性根的规则因语言/运行时而异。例如,在某些边缘情况下处理C#/ CLR存在细微差别。

答案 1 :(得分:3)

正如其他人所指出的那样,当一个对象既不能直接作为变量的值也不能间接访问时,也可以收集该对象,因为另一个可访问的对象具有对它的引用。让我们一次看几行代码,看看对象引用图随着时间的推移是什么样的。

  A a1=null;               // 1
  A a2=new A(new A(null)); // 2
  A a3=new A(a2);          // 3
  a1=a3;                   // 4

第1行没有做太多,我们甚至可以消除它并将第4行更改为A a1 = a3而没有任何不同的结果。第2行将a2的值设置为对A的新实例的引用,将其命名为α,其a字段是对A的第二个新实例的引用称之为β。第3行创建A的新实例,将其称为γ,其a字段是对α的引用。因此,所有α,β和γ都被引用。第4行使a1的值成为对γ的引用。请注意:

  • α可以a2直接访问,只要γ可以访问就可以间接访问;
  • 只要α可以访问,
  • β可通过α间接访问;和
  • γ可以a1a3直接访问。

first memory state

接下来,

  a1.a=new A(null);        // 5

a1.a = new A(null)将γ的a字段更新为A的新实例,将其称为δ。现在:

  • α可以a2直接访问,而不能通过其他任何方式间接访问;
  • β只能通过γ间接获得;
  • γ可以a1a3直接访问;和
  • δ只能通过α间接访问。

second memory state

最后,

  a2.a=null;               // 6

现在删除了对β的最后剩余引用。仍然提到α,γ和δ。

  • α可以a2;
  • 直接访问
  • β根本无法直接或间接访问;
  • γ可以a1a3直接访问;和
  • δ只能通过γ间接访问。

final memory state

因为β根本不可访问,所以它是垃圾收集的候选者。 System.gc()的javadoc说:

  

运行垃圾收集器。调用此方法表明Java   虚拟机花费精力来按顺序回收未使用的对象   使他们目前占用的内存可用于快速重用。   当控制从方法调用返回时,虚拟机具有   尽最大努力回收所有丢弃的物品。

因此,当我们到达

  System.gc(); // 7

系统可能,但不需要,收集β。可以安全地假设β将通过“尽最大努力回收所有丢弃的物体”来收集,但这不是保证。

答案 2 :(得分:0)

检查以下评论:

public static void main(String args[]){
  A a1=null;
  A a2=new A(new A(null));  //a2.a = new A(null); //line 3
  A a3=new A(a2);           //a3.a = a2
  a1=a3;                    //a1 = a3
  a1.a=new A(null);         //a1.a = a3.a = null; a2 is still referenced
  a2.a=null;  //Line12      //a2.a = null => object at line 3 is cleared after gc
                            //because it is not referenced anymore by any variable.
  System.gc();
}