在下面的代码中,在运行时只有一个对象从堆内存中清除(通过调用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();
}
}
答案 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
直接访问,只要γ可以访问就可以间接访问; a1
和a3
直接访问。
接下来,
a1.a=new A(null); // 5
a1.a = new A(null)
将γ的a
字段更新为A
的新实例,将其称为δ。现在:
a2
直接访问,而不能通过其他任何方式间接访问; a1
和a3
直接访问;和
最后,
a2.a=null; // 6
现在删除了对β的最后剩余引用。仍然提到α,γ和δ。
a2
; a1
和a3
直接访问;和
因为β根本不可访问,所以它是垃圾收集的候选者。 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();
}