[Java效能剖析]Sun JVM記憶體管理和垃圾回收

handawei_5發表於2010-05-13

 記憶體管理和垃圾回收是JVM非常關鍵的點,對Java效能的剖析而言,瞭解記憶體管理和垃圾回收的基本策略非常重要。本篇對Sun JVM 6.0的記憶體管理和垃圾回收做大概的描述。

      1.記憶體管理
      在程式執行過程當中,會建立大量的物件,這些物件,大部分是短週期的物件,小部分是長週期的物件,對於短週期的物件,需要頻繁地進行垃圾回收以保證無用物件儘早被釋放掉,對於長週期物件,則不需要頻率垃圾回收以確保無謂地垃圾掃描檢測。為解決這種矛盾,Sun JVM的記憶體管理採用分代的策略。
      1)年輕代(Young Gen):年輕代主要存放新建立的物件,記憶體大小相對會比較小,垃圾回收會比較頻繁。年輕代分成1個Eden Space和2個Suvivor Space(命名為A和B)

  • 當物件在堆建立時,將進入年輕代的Eden Space。
  • 垃圾回收器進行垃圾回收時,掃描Eden Space和A Suvivor Space,如果物件仍然存活,則複製到B Suvivor Space,如果B Suvivor Space已經滿,則複製 Old Gen
  • 掃描A Suvivor Space時,如果物件已經經過了幾次的掃描仍然存活,JVM認為其為一個Old物件,則將其移到Old Gen。
  • 掃描完畢後,JVM將Eden Space和A Suvivor Space清空,然後交換A和B的角色(即下次垃圾回收時會掃描Eden Space和BSuvivor Space。

      我們可以看到:Young Gen垃圾回收時,採用將存活物件複製到到空的Suvivor Space的方式來確保不存在記憶體碎片,採用空間換時間的方式來加速記憶體垃圾回收。
      2)年老代(Tenured Gen):年老代主要存放JVM認為比較old的物件(經過幾次的Young Gen的垃圾回收後仍然存在),記憶體大小相對會比較大,垃圾回收也相對沒有那麼頻繁(譬如可能幾個小時一次)。年老代主要採用壓縮的方式來避免記憶體碎片(將存活物件移動到記憶體片的一邊),當然,有些垃圾回收器(譬如CMS垃圾回收器)出於效率的原因,可能會不進行壓縮。
      3)持久代(Perm Gen):持久代主要存放類定義、位元組碼和常量等很少會變更的資訊,關於這塊的垃圾回收策略可以參考我的另一篇BLOG《Tomcat Context reloadabled 與 OutOfMemory(PermSpace) 》。
      Class data sharing (CDS)( http://java.sun.com/j2se/1.5.0/docs/guide/vm/class-data-sharing.html)是JDK5新引入的特性,採用在虛擬機器之間共享一些class定義資訊(bootstrapClassLoader載入的類)的方式提速JVM的啟動和記憶體的佔用,主要用於客戶端,如果需要對類進行instrutment,最好把CDS關閉。(預設情況下,JVM的server模式會關閉CDS,client模式會開啟CDS)

-Xshare:off
Disable class data sharing.
-Xshare:on
Require class data sharing to be enabled. If it could not be enabled for various reasons, print an error message and exit.
-Xshare:auto
The default; enable class data sharing whenever possible.

 

       我們通過JConsole截圖看看上面這幾個區的顯示(下圖),從左到右分別是EdenSpace、A Suvivor Space、Tenured Gen、Code Cache、Perm Gen(shared-wr)、Perm Gen(shared-ro)、Perm Gen

      2.垃圾回收策略
      評估垃圾回收策略的兩個重要度量是:

  • 吞吐量(Throughput ):JVM花費在垃圾回收上的時間越長,則吞吐量越低
  • 暫停時間(Pause time):JVM垃圾回收過程當中有一個暫停期,在暫停期間,應用程式不能執行,暫停時間是暫停期的長度

      非常遺憾的是,一般這兩個指標是相互衝突的,改善其中一個會影響到另外一個,根據情景的不同我們決定是優先考慮吞吐量還是暫停時間,對於需要實時響應的應用,我們需要優先考慮暫停時間,對於後臺執行應用,我們需要優先考慮吞吐量。
      在考察各種垃圾回收器之前,我們需要了解一下幾個重要的策略

  • 並行(Parallel):並行表示使用多個執行緒同時進行垃圾回收的工作,此策略一般會從同時改善暫停時間和吞吐量,在有多CPU核心的伺服器上,這是基本上我們要使用的策略。
  • 併發(Concurrent):並行表示垃圾回收器的一些工作(譬如垃圾標記)與應用程式同時進行,這將更進一步縮短暫停時間,需要注意的是,同時垃圾回收器的複雜性會大大增大,基本上是會降低吞吐量,
  • 記憶體碎片處理:有不壓縮、壓縮和拷貝三種策略,從空間上講,拷貝將花費更多的記憶體(譬如如上記憶體管理的Young Gen,需要維持一個額外的Suvivor空間),從時間上來講,不壓縮會減低建立物件時的記憶體分配效率,在垃圾回收上,拷貝策略會比壓縮策略更高效。

      Sun JVM有4垃圾回收器:

  • Serial Collector:序列垃圾回收器,垃圾回收器對Young Gen和Tenured Gen都是使用單線的垃圾回收方式,對Young Gen,會使用拷貝策略避免記憶體碎片,對Old Gen,會使用壓縮策略避免記憶體碎片。基本上,在對核心的伺服器上應該避免使用這種方式。在JVM啟動引數中使用-XX:+UseSerialGC啟用Serial Collector。
  • Parallel Collector:併發垃圾回收器,垃圾回收器對Young Gen和Tenured Gen都是使用多執行緒並行垃圾回收的方式,對Young Gen,會使用拷貝策略避免記憶體碎片,對Old Gen,會使用壓縮策略避免記憶體碎片。在JVM啟動引數中使用-XX:+UseParallelGC啟用Parallel Collector。
  • Parallel Compacting Collector:並行壓縮垃圾回收器,與Parallel Collector垃圾回收類似,但對Tenured Gen會使用一種更有效的垃圾回收策略,此垃圾回收器在暫停時間上會更短。在JVM啟動引數中使用-XX:+UseParallelOldGC啟用Parallel Compacting Collector。
  • Concurrent Mark-Sweep (CMS) Collector:併發標誌清除垃圾回收器,對Young Gen會使用與Parallel Collector同樣的垃圾回收策略,對Tenured Gen,垃圾回收的垃圾標誌執行緒與應用執行緒同時進行,而垃圾清除則需要暫停應用執行緒,但暫停時間會大大縮減,需要注意的是,由於垃圾回收過程更加複雜,會降低總體的吞吐量。

相關文章