3

有人问了我一个问题(在此站点http://scjptest.com/上):在此代码示例中,有多少对象符合垃圾回收条件,位于 // some code goes here?

class A {
    private B b;
    public A() {
        this.b = new B(this);
    }
}

class B {
    private A a;
    public B(A a) {
        this.a = a;
    }
}

public class Test { 
    public static void main(String args[]) {
        A aa = new A();
        aa = null;
        // some code goes here
    }
}

正确答案是:“a 和 b 引用的对象符合垃圾回收条件。”。但为什么?它们包含相互的循环引用,它们可以相互访问。

谢谢!

4

4 回答 4

5

它们包含相互的循环引用,它们可以相互访问。

是的,但它们不再可以从其他任何地方访问,因此无法再在程序中看到和使用它们。

早期的 GC 在收集此类自引用对象组时遇到了问题,但对于现代分代收集器来说,这是一个已解决的问题。

简而言之,GC 可以遍历来自已知静态和堆栈对象的引用网络,或者

  • 将找到的所有对象复制到新的内存池,自动留下任何“死”对象(这是“年轻一代”策略),或者
  • 标记找到的所有对象,这样一旦遍历整个引用网络,它就可以删除所有未标记的对象(这是“老/老一代”策略)。
于 2011-04-27T09:14:38.003 回答
2

那是因为 java 有一个分代垃圾收集器,所以它可以收集在它们之间有引用的对象组,但不能从应用程序收集。

这里有更深入的解释。

据我记得(我可能错了),旧版本的 java(例如 java 1.2)并非如此,其中必须手动清除循环依赖项才能让 GC 收集它们。

于 2011-04-27T09:12:58.590 回答
2

如果无法从任何线程访问该“岛”(或组)中的任何对象,GC 可以删除所谓的“对象岛”!

在您的示例中,您创建了一个对象 A,该对象 A 具有指向另一个对象 B 的链接。但是对象 B 不是任何人都希望 A“引用”的。您可以将这两个对象视为一个岛。当 A 消失时,GC 将足够聪明地确定 B 不能被任何其他线程引用,因此您将删除 2 个对象。

于 2011-04-27T09:59:14.857 回答
1

让我们绘制场景以清楚地理解。

A a = new A() 

a ------------this 指向对象------------> A() "第一个对象"

在 A() 的构造函数中,您实例化 B()“第二个对象”的实例

B 类实例内部只是指向上面的 A(),因此没有创建新对象。在您将空值分配给“a”变量时,没有引用将指向 A() 对象。现在您有 2 个符合 GC 条件的对象。不要担心类相互引用,只需关注 main 方法中的声明。

于 2011-08-01T08:36:54.947 回答