Java 采访问题:符合垃圾收集条件的对象

Java 采访问题:符合垃圾收集条件的对象,java,garbage-collection,Java,Garbage Collection,给出以下代码: class A { Boolean b; A easyMethod(A a){ a = null; return a; } public static void main(String [] args){ A a1 = new A(); A a2 = new A(); A a3 = new A(); a3 = a1.easyMethod(a2);

给出以下代码:

class A {
    Boolean b;
    A easyMethod(A a){
        a = null;
        return a;
    }
    public static void main(String [] args){
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a3 = a1.easyMethod(a2);
        a1 = null;
        // Some other code 
    }
}
问题是在
//其他一些代码之前,有多少对象符合垃圾收集的条件

正确的答案是(至少这是面试官的答案):2-布尔值
b
,因为它是一个包装器和
a1

你能告诉我为什么
a2
a3
没有被垃圾收集吗

以后编辑:

  • 好的,我想我现在明白了。起初有点困惑,但现在我确信面试官是错的。我最初的错误是,起初我并不认为java只是通过值,所以不可能从一个函数中把A2 null作为参数,因为A2实际上是A2的拷贝。李>
  • 布尔b的部分确实很明显
谢谢你的回答,我会在那之后发送一些面试反馈:)。

提供
a1。go(a2)
实际上是
a1。easyMethod(a2)
,答案确实是2,但不是你列出的那些。正如Bozho正确指出的,
b
没有初始化,因此它不引用任何对象。在注释处符合垃圾收集条件的两个对象是最初由
a1
a3
引用的对象


a1
明显为空,并且将
a3
重新分配给
a1.easyMethod(a2)
的返回值,该值为空。但是,
a2
不受方法调用的影响,因此只有引用
a2
的副本被传递给方法。即使副本设置为空,也不会影响原始
a2

的值,假设
go
应为
easyMethod
,其工作原理如下

class A {
    Boolean b;
    A easyMethod(A a){
        a = null; // the reference to a2 was passed in, but is set to null
                  // a2 is not set to null - this copy of a reference is!
        return a; // null is returned
    }
    public static void main(String [] args){
        A a1 = new A(); // 1 obj
        A a2 = new A(); // 2 obj
        A a3 = new A(); // 3 obj
        a3 = a1.go(a2); // a3 set to null and flagged for GC - see above for why
        a1 = null; // so far, a1 and a3 have been set to null and flagged
        // Some other code 
    }
}
有两个对象可以进行垃圾收集(a1和a3)
b
不是空的,因为它只是对null的引用。从未制作过
Boolean

为了避开
//其他一些代码可能是什么的空洞微妙之处,我将这个问题改写为以下内容:

记录并解释以下输出:

class A {
    int i;
    A(int i) { this.i = i; }
    public String toString() { return ""+i; }
    A go(A a){
        a = null; // the reference to a2 was passed in, but is set to null
                  // a2 is not set to null - this copy of a reference is!
        return a; // null is returned
    }
    public static void main(String [] args){
        A a1 = new A(1); // 1 obj
        A a2 = new A(2); // 2 obj
        A a3 = new A(3); // 3 obj
        a3 = a1.go(a2); // a3 set to null and flagged for GC - see above for why
        a1 = null; // so far, a1 and a3 have been set to null and flagged

        test(a1);
        test(a2);
        test(a3);

    }
    static void test(A a) {
        try { System.out.println(a); } 
        catch(Exception e) { System.out.println((String)null); }
    }
}
和输出:

c:\files\j>javac A.java

c:\files\j>java A
null
2
null
后续结果是,在这一点上,a1和a3符合GC的条件,而a2则不符合


这个问题的教训是“将对象引用传递给方法并将该引用设置为null不会导致原始引用为null”。这是面试官试图测试的知识。

首先,面试官对布尔值的理解是错误的——没有由该代码创建的对象,因此没有任何东西需要垃圾收集


变量
b
a2
称为垃圾收集是不正确的。对象是垃圾收集的,而不是变量。如果范围内变量引用对象,则无法对其进行垃圾收集。简单地说,只有当对象不再被任何变量引用时,它才能被垃圾收集

因此,我们在这段代码中创建了三个实例。它们开始时由
a1
等引用。但由于变量引用发生变化,我将对象实例称为a1、A2和A3。既然您还没有显示
go
方法的定义,我就假设它是对
easyMethod
的调用

由于变量a1被重新分配为null,因此没有任何内容指向实例a1,因此可以对其进行垃圾收集

由于变量a2从未被重新分配(easyMethod中的分配不会影响原始变量),因此实例a2不能被垃圾收集

由于
easyMethod
始终返回
null
,并且a3被分配了该方法的结果,因此没有任何内容指向实例a3,因此它也可以被垃圾收集

你能给我解释一下为什么吗 a3也没有被垃圾收集 ?


因为a2和a3不是对象。它们是变量。变量不可收集,原因很简单,它们不可分配。

对于a2的原始引用,它实际上完全取决于“其他代码”中发生的情况。如果“其他代码”不使用a2或a3,则原始a2对象有资格进行垃圾收集


这是因为运行时不必关心词法范围。它只需要知道一个对象再也不能被引用了。因此,如果“其他一些代码”没有使用a2或a3,那么它们指向的对象将无法再次被引用,因此已可用于垃圾收集。

您的问题相当混乱

垃圾收集器处理对象,而不是变量。 所以,当你说a2符合GC的条件时,它毫无意义。 您应该说a2在N行引用的对象符合N+M行的GC

在您的示例中,只有3个对象被实例化。这是第一次创建和最后一次创建符合GC条件的实例

问题是在
//其他代码之前有多少对象符合垃圾收集条件。

这个问题毫无意义

垃圾收集器在运行时根据那里可用的信息进行操作。可达性由存储在寄存器、线程堆栈和全局变量中的全局根决定。寄存器和堆栈的内容是许多编译阶段的最终结果,这些编译阶段完全破坏了代码。源代码中的词法范围和行号的概念已经不存在了,因此询问GC在源代码中的某些点上可能看到什么是毫无意义的,因为这些点在GC的世界中并不存在

因此,我们必须首先假设源代码和生成的代码之间存在直接对应关系,否则我们甚至无法理解po