簡體   English   中英

面試題:符合垃圾回收條件的對象

[英]Interview question: Objects eligible for 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 
    }
}

問題是在// Some other code之前// Some other code多少對象可以進行垃圾回收。

那么正確答案是(至少那是面試官的答案): 2 - 布爾值b因為它是一個包裝器和a1

你能解釋一下為什么a2a3沒有被垃圾收集嗎?

稍后編輯:

  • 好的,我想我現在明白了。 起初有點困惑,但現在我確信面試官是錯的。 我最初的錯誤是,一開始我沒有考慮 Java 只是按值傳遞,所以不可能從以“a2”作為參數的函數內部使 a2 為空,因為 a2 實際上是 a2 的副本。
  • 布爾 b 的部分確實很明顯。

感謝您的回答,之后我會發送一些面試反饋:)。

假設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

為了解決// 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); }
    }
}

和輸出:

c:\files\j>javac A.java

c:\files\j>java A
null
2
null

后續是,在這一點上,a1 和 a3 有資格進行 GC,而 a2 沒有。

這個問題的教訓是“將對象引用傳遞給方法並將該引用設置為 null 不會導致原始引用為 null”。 這就是面試官試圖測試的知識。

如果a1.go(a2)實際上是a1.easyMethod(a2) ,答案確實是 2,但不是您列出的那些。 正如 Bozho 正確指出的那樣, b沒有被初始化,所以它不引用任何對象。 在注釋處符合垃圾回收條件的兩個對象是最初由a1a3引用的對象。

a1顯然被清空了,而a3被重新賦值給a1.easyMethod(a2)的返回值,它是空的。 但是, a2不受方法調用的影響,因為Java 是按值傳遞的,因此只將引用a2的副本傳遞給該方法。 即使將副本設置為 null,也不會影響原始a2的值。

對於 a2 的原始引用,它實際上完全取決於“其他代碼”中發生的情況。 如果“其他代碼”不使用 a2 或 a3,則原始 a2 對象有資格進行垃圾回收。

那是因為運行時不必關心詞法范圍。 它只需要知道一個對象永遠不會被再次引用。 因此,如果“一些其他代碼”不使用 a2 或 a3,則它們指向的對象將永遠不會被再次引用,因此已經可用於垃圾收集。

首先,面試官對布爾值的看法是錯誤的——這段代碼沒有創建這樣的對象,所以沒有什么可以垃圾收集的。

說像ba2這樣的變量被垃圾收集是不正確的。 對象是垃圾收集的,而不是變量。 如果范圍內變量引用了一個對象,則它不能被垃圾收集。 簡單地說,只有當一個對象不再被任何變量引用時,它才可以被垃圾回收。

所以我們在這段代碼中創建了三個 A 實例。 它們開始由a1等引用,但由於變量引用發生變化,我將對象實例稱為 A1、A2 和 A3。 由於您尚未顯示go方法的定義,因此我假設它是對easyMethod的調用。

由於變量 a1 被重新賦值為 null,沒有任何東西指向實例 A1,因此它可以被垃圾收集。

由於變量 a2 永遠不會被重新賦值( easyMethod中的賦值不會影響原始變量),因此實例 A2 不能被垃圾收集。

由於easyMethod總是返回null並且 a3 被分配了該方法的結果,所以沒有任何東西指向實例 A3,所以它也可以被垃圾收集。

你能解釋一下為什么 a2 和 a3 沒有被垃圾收集嗎?

因為 a2 和 a3 不是對象。 它們是變量。 變量不可收集的原因很簡單,它們不可分配。

問題是在// Some other code.之前// Some other code.多少對象可以進行垃圾回收// Some other code.

這個問題是無稽之談。

垃圾收集器在運行時根據那里可用的信息進行操作。 可達性由存儲在寄存器、線程堆棧和全局變量中的全局根決定。 寄存器和堆棧的內容是許多編譯階段的高潮,這些階段完全破壞了代碼。 源代碼中的詞法范圍和行號的概念不再存在,因此詢問 GC 在源代碼中的某些點可能會看到什么是荒謬的,因為這些點在 GC 的世界中不存在。

所以我們必須首先假設源代碼和生成的代碼之間有直接對應關系,否則我們甚至無法理解問題所指的代碼中的點。 我想不出任何在實踐中實際執行此操作的工作 VM,事實上,Java 可能具有甚至無法以這種方式編譯為字節碼的高級語言結構。

接下來,我們必須假設本地引用在堆棧上的保存方式的模型。 與其假設一些定義不明確的模型來得出隨機答案,我將展示模型的選擇使我們能夠得出從“沒有任何東西適合 GC”到“一切都適合 GC”的答案。 這一系列合理的答案確實突出了這個問題的糟糕程度。

考慮一個簡單的模型,其中中間值(所有子表達式的結果以及變量)被壓入堆棧直到函數結束。 Windows Phone 7 上的簡單編譯器和虛擬機(如 HLVM 和 .NET)在實踐中實際上是這樣工作的,盡管這會保留引用的時間超過必要的時間。 在這個模型中,每個new A()將一個引用壓入堆棧,直到函數返回為止,因此所有三個都可以在所討論的點從堆棧中訪問,因此沒有任何東西可以進行垃圾回收。

考慮一個模型,其中引用在第一個點從堆棧中被刪除,而這些點永遠不會被再次讀取。 這更接近於 .NET 和 OCaml 等生產虛擬機的工作方式,除了它們在可能的情況下將本地引用保留在寄存器中,否則會溢出到函數調用堆棧幀中的預分配條目,否則會覆蓋死局部變量以最小化堆棧幀的大小。 使用此模型,所有引用都在問題點處失效,因此無法訪問,因此,所有三個新分配的對象以及args數組和它引用的所有字符串都有資格進行垃圾回收。

因此,我們不僅證明了nothing答案everything合理的,而且我們甚至引用了實現這些模型的真實工作虛擬機和垃圾收集器,因此我們的預測是可測試的。

如果我在面試中被問到這個問題,我會通過解釋其中的一些內容來測試面試官。 如果他們表現出對學習的興趣而反應良好,那么我仍然會對這份工作感興趣。 如果他們試圖為自己定義不明確的假設和無法檢驗的預測辯護而做出不好的反應,那么我就不想與他們合作。

你的問題很困惑。

垃圾收集器作用於對象,而不是變量。 因此,當您說 a2 有資格進行 GC 時,它沒有任何意義。 您應該說在第 N 行由 a2 引用的對象有資格在第 N+M 行進行 GC。

在您的示例中,您只有 3 個對象被實例化。 是第一個 create A 和最后一個 create A 實例符合 GC 條件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM