面试问题:符合垃圾收集条件的对象

And*_*anu 18 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);
        a1 = null;
        // Some other code 
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是之前有多少对象符合垃圾收集的条件// Some other code.

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

你能帮我解释一下为什么a2a3不是垃圾收集?

后期编辑:

  • 好的,我想我现在明白了.起初有点混乱,但现在我确信面试官是错的.我最初的错误是,起初我并不认为Java只是通过值传递,因此不可能从以"a2"作为参数的函数内部生成a2 null,因为a2实际上是a2的副本.
  • 布尔b的部分确实非常明显.

谢谢你的回答,我会在那之后发一些面试反馈:).

cor*_*iKa 19

假设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 
    }
}
Run Code Online (Sandbox Code Playgroud)

两个对象有资格进行垃圾回收(a1和a3).b不是因为它只是对null的引用.没有Boolean做过.

为了解决// Some other code可能存在的愚蠢微妙之处,我将问题重新改写为以下内容:

判定并解释以下输出:

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); }
    }
}
Run Code Online (Sandbox Code Playgroud)

并输出:

c:\files\j>javac A.java

c:\files\j>java A
null
2
null
Run Code Online (Sandbox Code Playgroud)

后续的是,那时a1和a3符合GC的条件,而a2则没有.

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


Pét*_*rök 7

提供的a1.go(a2)实际意思是a1.easyMethod(a2),答案确实是2,而不是你列出的.正如Bozho正确指出的那样,b没有初始化,所以它没有提到任何对象.在注释点有资格进行垃圾收集的两个对象是最初由a1和引用的对象a3.

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


Mar*_*ers 7

对于a2的原始拒绝,它实际上完全取决于"其他一些代码"中发生的事情.如果"某些其他代码"不使用a2或a3,则原始a2对象可以进行垃圾回收.

那是因为运行时不必关心词法范围.它只需要知道永远不能再引用一个对象.因此,如果"某些其他代码"不使用a2或a3,则它们指向的对象永远不会再次引用,因此已经可用于垃圾回收.


Dav*_*sta 5

首先,访谈者对布尔值是错误的 - 这个代码没有创建这样的对象,所以没有什么可以被垃圾收集.

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

因此,我们在此代码中创建了三个A实例.它们开始引用a1等等,但由于变量引用更改,我将对象实例称为A1,A2和A3.由于你没有显示go方法的定义,我将假设它是一个调用easyMethod.

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

由于变量a2永远不会被重新分配(赋值in easyMethod不影响原始变量),因此不能对实例A2进行垃圾回收.

由于easyMethod始终返回null并且a3被赋予该方法的结果,因此没有任何东西指向实例A3,因此它也可以被垃圾收集.

  • "如果范围内变量引用了一个对象,那么它就不能被垃圾收集".编译器中的活动分析在其作用域结束之前从堆栈帧中删除死变量.如果它们不在堆栈中,则它们不是GC的全局根,因此它们所指的可以是垃圾收集. (3认同)

Joh*_*ohn 5

你可以请我解释一下为什么a2和a3没有被垃圾收集?

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