垃圾收集器與Java程式設計

azz發表於2007-08-24
垃圾收集器與Java程式設計[@more@]  垃圾收集器(Garbage Collector,GC)對Java程式設計師來說,基本上是透明的,但是一個優秀的Java程式設計師必須瞭解GC的工作原理、如何最佳化GC的效能、如何與GC進行有限的互動,因為有一些應用程式對效能要求較高,例如嵌入式系統、實時系統等,只有全面提升記憶體的管理效率 ,才能提高整個應用程式的效能。本篇文章首先簡單介紹GC的工作原理之後,然後再對GC的幾個關鍵問題進行深入探討,最後提出一些Java程式設計建議,從GC角度提高Java程式的效能。

  GC的基本原理

  Java的記憶體管理實際上就是物件的管理,其中包括物件的分配和釋放。

  對於程式設計師來說,分配物件使用new關鍵字;釋放物件時,只要將物件所有引用賦值為null,讓程式不能夠再訪問到這個物件,我們稱該物件為"不可達的"。GC將負責回收所有"不可達"物件的記憶體空間。

  對於GC來說,當程式設計師建立物件時,GC就開始監控這個物件的地址、大小以及使用情況。通常,GC採用有向圖的方式記錄和管理堆(heap)中的所有物件(詳見 參考資料1 )。透過這種方式確定哪些物件是"可達的",哪些物件是"不可達的"。當GC確定一些物件為"不可達"時,GC就有責任回收這些記憶體空間。但是,為了保證GC能夠在不同平臺實現的問題,Java規範對GC的很多行為都沒有進行嚴格的規定。例如,對於採用什麼型別的回收演算法、什麼時候進行回收等重要問題都沒有明確的規定。因此,不同的JVM的實現者往往有不同的實現演算法。這也給Java程式設計師的開發帶來行多不確定性。本文研究了幾個與GC工作相關的問題,努力減少這種不確定性給Java程式帶來的負面影響。

  增量式GC( Incremental GC )

  GC在JVM中通常是由一個或一組程式來實現的,它本身也和使用者程式一樣佔用heap空間,執行時也佔用CPU。當GC程式執行時,應用程式停止執行。因此,當GC執行時間較長時,使用者能夠感到Java程式的停頓,另外一方面,如果GC執行時間太短,則可能物件回收率太低,這意味著還有很多應該回收的物件沒有被回收,仍然佔用大量記憶體。因此,在設計GC的時候,就必須在停頓時間和回收率之間進行權衡。一個好的GC實現允許使用者定義自己所需要的設定,例如有些記憶體有限有裝置,對記憶體的使用量非常敏感,希望GC能夠準確的回收記憶體,它並不在意程式速度的放慢。另外一些實時網路遊戲,就不能夠允許程式有長時間的中斷。增量式GC就是透過一定的回收演算法,把一個長時間的中斷,劃分為很多個小的中斷,透過這種方式減少GC對使用者程式的影響。雖然,增量式GC在整體效能上可能不如普通GC的效率高,但是它能夠減少程式的最長停頓時間。


  Sun JDK提供的HotSpot JVM就能支援增量式GC。HotSpot JVM預設GC方式為不使用增量GC,為了啟動增量GC,我們必須在執行Java程式時增加-Xincgc的引數。HotSpot JVM增量式GC的實現是採用Train GC演算法。它的基本想法就是,將堆中的所有物件按照建立和使用情況進行分組(分層),將使用頻繁高和具有相關性的物件放在一隊中,隨著程式的執行,不斷對組進行調整。當GC執行時,它總是先回收最老的(最近很少訪問的)的物件,如果整組都為可回收物件,GC將整組回收。這樣,每次GC執行只回收一定比例的不可達物件,保證程式的順暢執行。

  詳解finalize函式

  finalize是位於Object類的一個方法,該方法的訪問修飾符為protected,由於所有類為Object的子類,因此使用者類很容易訪問到這個方法。由於,finalize函式沒有自動實現鏈式呼叫,我們必須手動的實現,因此finalize函式的最後一個語句通常是super.finalize()。透過這種方式,我們可以實現從下到上實現finalize的呼叫,即先釋放自己的資源,然後再釋放父類的資源。

  根據Java語言規範,JVM保證呼叫finalize函式之前,這個物件是不可達的,但是JVM不保證這個函式一定會被呼叫。另外,規範還保證finalize函式最多執行一次。

  很多Java初學者會認為這個方法類似與C++中的解構函式,將很多物件、資源的釋放都放在這一函式里面。其實,這不是一種很好的方式。原因有三,其一,GC為了能夠支援finalize函式,要對覆蓋這個函式的物件作很多附加的工作。其二,在finalize執行完成之後,該物件可能變成可達的,GC還要再檢查一次該物件是否是可達的。因此,使用finalize會降低GC的執行效能。其三,由於GC呼叫finalize的時間是不確定的,因此透過這種方式釋放資源也是不確定的。

  通常,finalize用於一些不容易控制、並且非常重要資源的釋放,例如一些I/O的操作,資料的連線。這些資源的釋放對整個應用程式是非常關鍵的。在這種情況下,程式設計師應該以透過程式本身管理(包括釋放)這些資源為主,以finalize函式釋放資源方式為輔,形成一種雙保險的管理機制,而不應該僅僅依靠finalize來釋放資源。

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

相關文章