物件引用是怎樣嚴重影響垃圾收集器的

azz發表於2007-08-24
物件引用是怎樣嚴重影響垃圾收集器的[@more@]  果您認為 Java 遊戲開發人員是 Java 程式設計世界的一級方程式賽車手,那麼您就會明白為什麼他們會如此地重視程式的效能。 遊戲開發人員幾乎每天都要面對的效能問題,往往超過了一般程式設計師考慮問題的範圍。哪裡可以找到這些特殊的開發人員呢?Java 遊戲社群就是一個好去處(參見 參考資料)。 雖然在這個站點可能沒有很多關於伺服器端的應用,但是我們依然可以從中受益,看看這些“惜位元如金”的遊戲開發人員每天所面對的,我們往往能從中得到寶貴的經驗。讓我們開始遊戲吧!

  物件洩漏

  遊戲程式設計師跟其他程式設計師一樣――他們也需要理解 Java 執行時環境的一些微妙之處,比如垃圾收集。垃圾收集可能是使您感到難於理解的較難的概念之一, 因為它並不能總是毫無遺漏地解決 Java 執行時環境中堆管理的問題。似乎有很多類似這樣的討論,它的開頭或結尾寫著:“我的問題是關於垃圾收集”。

  假如您正面遭遇記憶體耗盡(out-of-memory)的錯誤。於是您使用檢測工具想要找到問題所在,但這是徒勞的。您很容易想到另外一個比較可信的原因:這是 Java 虛擬機器堆管理的問題,而不會認為這是您自己的程式的緣故。但是,正如 Java 遊戲社群的資深專家不止一次地解釋的,Java 虛擬機器並不存在任何被證實的物件洩漏問題。實踐證明,垃圾收集器一般能夠精確地判斷哪些物件可被收集,並且重新收回它們的記憶體空間給 Java 虛擬機器。所以,如果您遇到了記憶體耗盡的錯誤,那麼這完全可能是由您的程式造成的,也就是說您的程式中存在著“無意識的物件保留(unintentional object retention)”。

  記憶體洩漏與無意識的物件保留

  記憶體洩漏和無意識的物件保留的區別是什麼呢?對於用 Java 語言編寫的程式來說,確實沒有區別。兩者都是指在您的程式中存在一些物件引用,但實際上您並不需要引用這些物件。一個典型的例子是向一個集合中加入一些物件以便以後使用它們,但是您卻忘了在使用完以後從集合中刪除這些物件。因為集合可以無限制地擴大,並且從來不會變小,所以當您在集合中加入了太多的物件(或者是有很多的物件被集合中的元素所引用)時,您就會因為堆的空間被填滿而導致記憶體耗盡的錯誤。垃圾收集器不能收集這些您認為已經用完的物件,因為對於垃圾收集器來說,應用程式仍然可以透過這個集合在任何時候訪問這些物件,所以這些物件是不可能被當作垃圾的。

  對於沒有垃圾收集的語言來說,例如 C++ ,記憶體洩漏和無意識的物件保留是有區別的。C++ 程式跟 Java 程式一樣,可能產生無意識的物件保留。但是 C++ 程式中存在真正的記憶體洩漏,即應用程式無法訪問一些物件以至於被這些物件使用的記憶體無法釋放且返還給系統。令人欣慰的是,在 Java 程式中,這種記憶體洩漏是不可能出現的。所以,我們更喜歡用“無意識的物件保留”來表示這個令 Java 程式設計師抓破頭皮的記憶體問題。這樣,我們就能區別於其他使用沒有垃圾收集語言的程式設計師。

  跟蹤被保留的物件

  那麼當發現了無意識的物件保留該怎麼辦呢?首先,需要確定哪些物件是被無意保留的,並且需要找到究竟是哪些物件在引用它們。然後必須安排好 應該在哪裡釋放它們。最容易的方法是使用能夠對堆產生快照的檢測工具來標識這些物件,比較堆的快照中物件的數目,跟蹤這些物件,找到引用這些物件的物件,然後強制進行垃圾收集。有了這樣一個檢測器,接下來的工作相對而言就比較簡單了:

  等待直到系統達到一個穩定的狀態,這個狀態下大多數新產生的物件都是暫時的,符合被收集的條件;這種狀態一般在程式所有的初始化工作都完成了之後。
  強制進行一次垃圾收集,並且對此時的堆做一份物件快照。
  進行任何可以產生無意地保留的物件的操作。
  再強制進行一次垃圾收集,然後對系統堆中的物件做第二次物件快照。
  比較兩次快照,看看哪些物件的被引用數量比第一次快照時增加了。因為您在快照之前強制進行了垃圾收集,那麼剩下的物件都應該是被應用程式所引用的物件,並且透過比較兩次快照我們可以準確地找出那些被程式保留的、新產生的物件。
  根據您對應用程式本身的理解,並且根據對兩次快照的比較,判斷出哪些物件是被無意保留的。
  跟蹤這些物件的引用鏈,找出究竟是哪些物件在引用這些無意地保留的物件,直到您找到了那個根物件,它就是產生問題的根源。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10901326/viewspace-965631/,如需轉載,請註明出處,否則將追究法律責任。

相關文章