繁体   English   中英

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

[英]Why does System.gc() recycle only one object in this code?

在下面的代码中,只有一个对象在运行时从堆内存中清除(通过调用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();
    }
  }

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

Java垃圾回收在对象可访问性上起作用。 如果对象是高度可访问的-也就是说,如果可以从可访问性或已知根(包括局部变量)的任何路径访问它,则该对象将无法回收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作品即使不使用传统的马克和扫描为基础的方法(这是在JVM中常见)作为规则同样适用于任何正常GC系统时:如果有机会的话可以将一个物体被访问,则无法回收。 相反,如果无法访问对象,则应将其回收。

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

正如其他人指出的那样,当一个对象既不能直接作为变量的值访问,也不能因为另一个可访问对象对其进行引用而间接访问时,就可以收集该对象。 让我们一次遍历几行代码,看看随着时间的推移对象引用图的外观。

  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

第一记忆状态

下一个,

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

a1.a = new A(null)更新γ是a领域是一个新的实例A ,δ调用它。 现在:

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

第二记忆状态

最后,

  a2.a=null;               // 6

现在,删除对β的最后一个引用。 仍然引用α,γ和δ。

  • α作为a2可直接访问;
  • β根本不能直接或间接访问;
  • 可以直接通过a1a3访问γ;
  • δ仅可通过γ间接访问。

最终记忆状态

由于根本无法访问β,因此它是垃圾收集的候选对象。 System.gc()的Javadoc说:

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

因此,当我们到达

  System.gc(); // 7

该系统可以但不要求收集β。 可以肯定地说,β将通过“尽最大努力回收所有丢弃的物体”来收集,但这并不是保证。

检查以下评论:

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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM