我认为尝试在 Java 中确定准确的 GC 收集时间是很可怕的,但无论如何..
.. Java 垃圾收集适用于对象可达性。如果一个对象是强可达的——也就是说,如果它可以从可达性或已知根的任何路径访问,包括局部变量——那么这个对象就不能被回收1。
考虑这个分解,其中 W、X、Y、Z 代表 A 的不同实例。我用来显示任何特定 A 的语法是instance {a-> instance}
wherea
指代成员变量。请记住,每个都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中进行回收。
只有 X 在线路上不再可达System.gc()
。然而,这并不意味着X 将被垃圾回收,即使有明确的 GC 调用 - 它只意味着 X 有资格在未来的某个时候被回收。
当然,一旦函数结束,所有局部变量都不再是可达根,并且没有一个 A 对象是强可达的,这使得它们都符合条件:)
1即使不使用传统的基于 Mark & Sweep 的方法(在 JVM 中常见),使用对象可达性来讨论 GC 仍然有效,因为相同的规则适用于任何正常运行的 GC 系统:如果有机会对象可以被访问过,无法回收。反过来说:如果一个对象不能被访问,那么它应该被回收。
2确定可达性根的规则因语言/运行时而异。例如,在某些边缘情况下,C#/CLR 处理存在细微差别。