2011-11-19 50 views
4

我剛纔讀這篇文章:The Truth About Garbage Collection爲什麼不能立即收集「隱形」物體?

在第「A.3.3隱形」闡述瞭如何當一個對象進入了invisible狀態。

在下面的代碼,分配給變量foo對象將離開try/catch塊之後成爲invisible並將remainly強烈引用直到run方法退出(這永遠不會發生,這是因爲while循環運行永遠)。

public void run() { 
    try { 
     Object foo = new Object(); 
     foo.doSomething(); 
    } catch (Exception e) { 
     // whatever 
    } 
    while (true) { // do stuff } // loop forever 
} 

據這篇文章中指出:

然而,有效的實現了JVM是不可能爲零 參考,當它超出範圍。

爲什麼這樣不高效?

我在解釋嘗試如下:

假設堆棧此方法包含四個元件,與現在不可見對象是在底部。
如果你想立即收集對象,你將不得不彈出並存儲三個元素,彈出並放棄第四個元素,然後將三個仍然有效的元素推回到堆棧上。
如果在控制流離開run方法後收集不可見對象,則VM可以簡單地彈出所有四個元素並丟棄它們。

+0

這是一個好主意,不要將不相關的代碼混合到同一個方法中。混淆的代碼也會混淆JVM。 –

回答

3

局部變量不在操作數堆棧上,而是在激活幀的局部變量區域中訪問,在通過aloadastore字節碼進行引用並對局部變量進行調零時不涉及任何推送和彈出。因爲它不需要

歸零是低效的:

  • 它不會導致立即垃圾收集週期
  • 零可能很快由另一值覆蓋由程序的邏輯所指示的。
  • 超出範圍意味着本地變量不再是垃圾回收的根集的一部分。因此,它在超出範圍之前所持有的價值 - 零或有效的參考 - 並不重要;它不會被檢查。

編輯:

在最後陳述一些意見。

事實上,在字節碼級別上,沒有範圍,局部變量槽可以保留爲根集的一部分,直到方法返回。當然,JVM實現可以確定局部變量插槽何時死亡(即,所有可能的方法返回路徑都不訪問變量或存儲)並且不認爲它是根集的一部分,但它是絕不要求這樣做。

+0

*超出範圍意味着局部變量不再是垃圾收集根集的一部分*這是不正確的,當一個對象實際上沒有被使用時,即當沒有更多的引用時至。例如'{Object a = new Object(); a.hashCode(); //提供不再有對它的垃圾材質的引用}。 – bestsss

+0

@bestsss,或許你想說點別的,因爲在這種形式下你的評論沒有多大意義? – chill

+0

JVM不瞭解Java定義的範圍。即使在字節碼中,括號{}也沒有定義範圍,實際上有'幀',但是它們不會限制範圍。在之前的評論中,即使在a.hashCode()之後存在無限循環,GC可以自由地收集'a',只要它不再被引用。至於編輯:JVM不需要收集任何東西,並且可以有沒有任何垃圾收集器的JVM。 – bestsss

0

非常簡單的答案是b/c效率低下。

有很多垃圾收集器算法,有些可能會積極收集。一些編譯器會在堆棧上進行分配,但最明顯的情況是:doSomething()實際上可能會保留(泄漏)對其他對象的引用。