简体   繁体   English

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

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

In the code below, only one object cleared from heap memory at runtime (by calling System.gc() ). 在下面的代码中,只有一个对象在运行时从堆内存中清除(通过调用System.gc() )。 Why is only one object recycled? 为什么只回收一个对象?

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

I think it's horrid to try and pin exact GC-collection times in Java, but anyway .. 我认为尝试用Java固定确切的GC收集时间是很可怕的,但是无论如何..

.. the Java garbage collection works on object reachability . Java垃圾回收在对象可访问性上起作用。 If an object is strongly reachable - that is, if it can be accessed by any path from a reachability or known root, which includes local variables - then the object cannot be reclaimed 1 . 如果对象是高度可访问的-也就是说,如果可以从可访问性或已知根(包括局部变量)的任何路径访问它,则该对象将无法回收1

Consider this breakdown, where W, X, Y, Z represent different instances of A. The syntax I use to show any particular A is instance {a-> instance} where a refers to the member variable. 考虑一下这种细分,其中W,X,Y,Z代表A的不同实例 。我用来显示任何特定A的语法是instance {a-> instance} ,其中a表示成员变量。 Remember each new creates a different instance, and that assignment of object values does not create new objects (as such the same Y object - now shared by a1 and a3 - is modified during a1.a=new A(null) assignment). 请记住,每个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}

So at the end the variables a1 and a3 - which are reachability roots - are "referencing" Y {a->Z} and a2 is "referencing" W {a->null} . 因此,最后变量a1和a3- 可达根 -是“引用” Y {a->Z}而a2是“引用” W {a->null} This means that W, Y and Z are all still strongly reachable (Z is strongly reachable through Y) and not considered eligible for reclamation in Java-land 2 . 这意味着W,Y和Z仍然都是很容易到达的(Z可以通过Y很大地到达),并且不适合在Java-land 2中进行回收。

Only X is no longer reachable at the System.gc() line. System.gc()行上仅X不再可访问。 However, this does not mean that X will be garbage collected, even with an explicit GC call - it only means that X is eligible to be reclaimed at some point in the future. 但是,这并不意味着X将是垃圾回收,即使有明确的GC调用-它只是意味着X是有资格在未来某个时间点进行回收。

Of course, as soon as the function ends, all the local variables are no longer reachability roots and none of the A objects are strongly reachable making them all eligible :) 当然,函数结束后,所有局部变量都不再是可访问性根,并且A对象都不是强可访问性,从而使它们都符合条件:)


1 Using object reachability to discuss a GC still works even when not using a traditional Mark & Sweep based approach (which is commonly found in JVMs) as the same rules apply to any correctly functioning GC system: if there is a chance an object can be accessed, it cannot be reclaimed. 1使用对象的可达性仍然讨论GC作品即使不使用传统的马克和扫描为基础的方法(这是在JVM中常见)作为规则同样适用于任何正常GC系统时:如果有机会的话可以将一个物体被访问,则无法回收。 And conversely: if an object cannot be accessed, then it should be reclaimed. 相反,如果无法访问对象,则应将其回收。

2 The rules to determine reachability roots differ by language/runtime. 2确定可达性根源的规则因语言/运行时间而异。 For instance, there are subtle differences with C#/CLRs handling in certain edge cases. 例如,在某些极端情况下,C#/ CLR处理存在细微的差异。

As others have pointed out, an object can be collected when it is accessible neither directly as the value of a variable nor indirectly because another accessible object has a reference to it. 正如其他人指出的那样,当一个对象既不能直接作为变量的值访问,也不能因为另一个可访问对象对其进行引用而间接访问时,就可以收集该对象。 Let's go through the lines of code a few at a time and look at what the object reference graph looks like over time. 让我们一次遍历几行代码,看看随着时间的推移对象引用图的外观。

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

Line 1 doesn't do much, and we could even eliminate it and change line 4 to A a1 = a3 without any different results. 第1行没有太大的作用,我们甚至可以消除它,并将第4行更改为A a1 = a3而没有任何不同的结果。 Line 2 sets a2 's value to a reference to a new instance of A , call it α, whose a field is a reference to a second new instance of A , call it β. 线2台a2的值到一个新的实例的引用A ,称之为α,其a字段是的第二个新的实例的引用A ,β调用它。 Line 3 creates a new instance of A , call it γ, whose a field is a reference to α. 第3行创建的新实例A ,称之为γ,它的a领域是α的参考。 Thus, all of α, β, and γ, are referenced. 因此,参考了所有的α,β和γ。 Line 4 makes a1 's value a reference to γ. 第4行将a1的值作为γ的参考。 Note that: 注意:

  • α is directly accessible as a2 , and indirectly accesssible as long as γ is accessible; α可以直接访问为a2 ,只要可以访问γ,则可以间接访问;
  • β is indirectly accessible through α, as long as α is accessible; 只要α可以访问,就可以通过α间接访问β; and
  • γ is directly accessible as a1 and a3 . 可以直接访问a1a3

第一记忆状态

Next, 下一个,

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

a1.a = new A(null) updates γ's a field to be a new instance of A , call it δ. a1.a = new A(null)更新γ是a领域是一个新的实例A ,δ调用它。 Now: 现在:

  • α is directly accessible as a2 , and not indirectly accessible through anything else; α可作为a2直接访问,而不能通过其他任何方式间接访问;
  • β is indirectly accessible only through γ; β只能通过γ间接访问;
  • γ is directly accessible as a1 and a3 ; 可以直接通过a1a3访问γ; and
  • δ is indirectly accessible only through α. δ只能通过α间接访问。

第二记忆状态

Finally, 最后,

  a2.a=null;               // 6

Now the last remaining reference to β is removed. 现在,删除对β的最后一个引用。 There are still references to α, γ, and δ. 仍然引用α,γ和δ。

  • α is directly accessible as a2 ; α作为a2可直接访问;
  • β is not accessible directly or indirectly at all; β根本不能直接或间接访问;
  • γ is directly accessible as a1 and a3 ; 可以直接通过a1a3访问γ; and
  • δ is indirectly accessible only through γ. δ仅可通过γ间接访问。

最终记忆状态

Because β is not accessible at all, it is a candidate for garbage collection. 由于根本无法访问β,因此它是垃圾收集的候选对象。 The javadoc for System.gc() says: System.gc()的Javadoc说:

Runs the garbage collector. 运行垃圾收集器。 Calling this method suggests that the Java virtual machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. 调用此方法表明,Java虚拟机将花费更多精力来回收未使用的对象,以使它们当前占用的内存可用于快速重用。 When control returns from the method call, the virtual machine has made its best effort to recycle all discarded objects. 当控制从方法调用返回时,虚拟机将尽最大努力回收所有丢弃的对象。

Thus, when we get to 因此,当我们到达

  System.gc(); // 7

the system may , but is not required to, collect β. 该系统可以但不要求收集β。 It's probably safe to assume that β would be collected by a “best effort to recycle all discarded objects,” but it's not a guarantee. 可以肯定地说,β将通过“尽最大努力回收所有丢弃的物体”来收集,但这并不是保证。

Check the comments below: 检查以下评论:

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