1. 程式人生 > >Java的GC機制及演算法

Java的GC機制及演算法

 

 

 

GC的階段

對每個物件而言,垃圾回收分為兩個階段:finalization和reclamation。 

  • finalization: 指執行這個物件的finalize的方法。
  • reclamation: 回收被這個物件使用的記憶體。

GC的過程的基本步驟 

  • 首先確認物件是不可達的,即將被回收。
  • 其次,如果物件有finalize方法,那麼物件被新增進finalization queue中;然後在某個時間點finalize方法被呼叫以釋放finalize中的資源。
  • 最後,回收物件佔用的記憶體。

關於finalize方法的問題 

  • finalize方法使得GC過程做了更多的事情,增加的GC的負擔。
  • 如果某個物件的finalize方法執行時間過長,它會使得其他物件的finalize方法被延遲執行。
  • finalize方法中如果建立了strong reference引用了其他物件,這會阻止此物件被GC。
  • finalize方法有可能以不可確定的順序執行(也就是說要在安全性要求嚴格的場景中儘量避免使用finalize方法)。
  • 不確保finalize方法會被及時呼叫,也許程式都退出了,但是finalize方法還沒被呼叫。

物件引用的型別 

  • Reference(or named Strong Reference)( 強引用):普通型別的引用。
  • SoftReference( 軟引用):被這種引用指向的物件,如果此物件沒要再被其他Strong Reference引用的話,可能在任何時候被GC。雖然是可能在任何時候被GC,但是通常是在可用記憶體數比較低的時候,並且在程式丟擲OutOfMemoryError之前才發生對此物件的GC。SoftReference通常被用作實現Cache的物件引用,如果這個物件被GC了,那麼他可以在任何時候再重新被建立。另外,根據JDK文件中介紹,實際JVM的實現是鼓勵不回收最近建立和最近使用的物件。SoftReference 
    類的一個典型用途就是用於記憶體敏感的快取記憶體。
  • WeakReference(弱引用):如果一個被WeakReference引用的物件,當沒要任何SoftReference和StrongReference引用時,立即會被GC。和SoftReference的區別是:WeakReference物件是被eagerly collected,即一旦沒要任何SoftReference和StrongReference引用,立即被清楚;而只被SoftReference引用的物件,不回立即被清楚,只有當記憶體不夠,即將發生OutOfMemoryError時才被清除,而且是先清除不常用的。SoftReference適合實現Cache用。WeakReference 類的一個典型用途就是規範化對映( canonicalized mapping )
  • PhantomReference(虛引用):當沒有StrongReference,SoftReference和WeakReference引用時,隨時可被GC。通常和ReferenceQueue聯合使用,管理和清除與被引用物件(沒有finalize方法)相關的本地資源。

衡量GC的指標(GC Metrics) 

  • Throughput(吞吐量):所有沒有花在執行GC上的時間佔總執行時間的比重。
  • Pauses(暫停):當GC在執行時程式的暫停次數。或者是在感興趣的暫停次數中,暫停的平均時長和最大時長。
  • Footprint(足跡?):當前使用的堆記憶體大小。
  • Promptness(及時性):不再使用的物件多久能被清除掉並釋放其記憶體。

通用GC演算法 
Java所使用的所有的GC演算法都是通用GC演算法概念的變種。 
通用GC演算法的假設: 

  • 最近建立的物件很可能很快就不可達了(unreachable,即可被回收了),比如方法內部宣告的本地變數,當程式執行出了本地變數的作用範圍後,本地變數引用的物件就很快不可達了。
  • 一個物件保持可達(reachable)的越久就越不可能被回收。

在Java GC中,物件被劃分為generations(代)或spaces(空間)。Java把物件分為young(年輕代),tenured(年老代)和perm(永久代)。在GC過程中,物件從一個space(空間)移動到另一個space。 
Object Spaces(物件空間) 

  • Young:年輕代中儲存著剛建立的物件,這個代中的物件能夠“minor” or “major” 收集中被回收。
  • Tenured:年老代中儲存著從年輕代中倖存下來的物件,只能夠在“major”中被回收。
  • Perm:永久代中儲存著JVM所需的物件,比如Class物件和Method物件,以及他們的位元組碼和內部字串等。對Perm中的物件GC意味著所有的Class都被解除安裝了。

每塊空間的大小由當前的對記憶體大小決定,並且能夠在執行時改變。每個空間之間的關係如下圖所示: 
 

Young Spaces(年輕空間) 

  • Eden space:儲存自從上次GC完畢之後新建立的物件,除了屬於Perm的物件。當minor collection發生時,Eden space中的物件或者GC清理掉,或者被移到survivor space。
  • Survivor spaces:這個空間中儲存的是自從上次GC倖存下來的young object。在minor GC中,這些物件或者被GC清理掉,或者被移到另外一個survivor空間中。

Minor collections和Major collections 

  • Minor collection當young space被佔滿時執行。它比major collections快,因為minor collection僅僅檢查major collection相應的一個子集物件。minor collection比major collection發生的頻率高。
  • Major collection當tenured space被佔滿時執行。他會清理tenured和young。

GC執行的三種方式 
在java5和java6中有4中垃圾回收的演算法,有一種演算法將不再支援,剩餘的三種垃圾回收演算法是:serial,throughput and concurrent low pause。 

  • Stop the world(停止所有程式的方式):在這種方式執行的GC,在GC完成前,JVM中的所有程式都不允許執行。Serial collector此時做minor和major收集。Throughput collector此時做major collector。
  • Incremental(增量執行方式):目前沒要Java GC演算法支援這種執行方式。GC以這種方式執行時,GC允許程式做一小段時間的工作,然後做垃圾回收工作。
  • Concurrent(並行執行):Throughput collector此時做minor collect,Concurrent low pause collector此時做minor和major收集。在這種執行方式下,GC和程式並行的執行,因此程式僅僅被短暫的暫停。

GC演算法 

  • Serial演算法: 使用-XX:+UseSerialGC開啟此演算法的GC。GC使用和應用程式相同的執行緒去做minor collection和major collection。
  • Throughput:使用-XX:+UseParallelGC開啟此演算法GC。GC使用多執行緒去做minor collection以減少程式停止的時間。但是對於major collection,還是使用同程式相同的執行緒去做。當具有多核cpu時,並且程式有大量的短生命週期的物件時,並且對程式停頓時間不限制時較好。
  • Concurrent Low Pause: 使用-XX:+UseConcMarkSweepGC開啟此演算法GC。使用多執行緒去做minor和major collection。當具有多核cpu,並且程式有大量的長生命週期的物件,並且對程式停頓時間有限制時,效果較好。

什麼時候發生GC 
GC發生的時刻受堆記憶體大小的影響。如果堆記憶體小,GC會執行的很快,但是又會很快的被填滿,因此GC比頻繁;如果堆記憶體很大,GC會執行的較慢,而且不會很快被填滿,因此執行的比較頻率比較低。 

基本的GC除錯 
throughput goal -XX:GCTimeRatio=n: 表示花費總時間百分之多少的CPU時間去執行程式。 
maximum pause time goal -XX:MaxGCPauseMillis=n:每次GC時程式暫停最多多少毫秒。 
footprint goal:如果其他目標都達到了,那麼首先減少heap size,直到前兩個goal不再滿足,然後再慢慢增加。直到滿足前面兩個goal。 
-Xms=n (starting) and -Xmx=n (maximum) heap size,這兩個引數應該都很熟悉,就是JVM使用的最小堆記憶體數和最大堆記憶體數。 
-XX:MinHeapFreeRatio=n, -XX:MaxHeapFreeRatio=n:最小和最大的空閒堆記憶體和被使用堆記憶體的比例。當空閒堆記憶體比例小於MinHeapFreeRatio時,記憶體空間開始擴充套件。當空閒堆記憶體比例大於MaxHeapFreeRatio時,記憶體空間開始減小。 
-XX:NewSize=n, -XX:MaxNewSize=n:預設的young space的大小(包括eden + survivor 1 + survivor 2)。 
-XX:NewRatio=n:young和tenured的比例。 
-XX:SurvivorRatio=n:每個survivor space 和 eden之間的比例。 
-XX:MaxPermSize=n:perm的最大size。 
-XX:TargetSurvivorRatio=n:每次GC之後倖存下來的空間的目標比例。 
-XX:+DisableExplicitGC:當此引數開啟時,在程式中呼叫System.gc()將會不起作用。預設是off。 
-XX:+ScavengeBeforeFullGC:當開啟此引數時,在每次major collection時先執行一次minor collection。預設開啟。 
-XX:+UseGCOverheadLimit:當開啟此引數時,如果總執行時間的98%的時間都在做GC,則丟擲OutOfMemmoryError。預設開啟。 

參考資料:http://java.ociweb.com/mark/other-presentations/JavaGC.pdf