JVM gc

調整JVM GC(Garbage Collection),可以極大的減少由於GC工作,而導致的程序運行中斷方面的問題,進而適當的提高Java程序的工作效率。但是調整GC是以個極爲複雜的過程,由於各個程序具備不同的特點,如:web和GUI程序就有很大區別(Web可以適當的停頓,但GUI停頓是客戶無法接受的),而且由於跑在各個機器上的配置不同(主要cup個數,內存不同),所以使用的GC種類也會不同。接下來,我簡單介紹一下如何調整GC。

    首先說一下如何監視GC,你可以使用我以前文章中提到的JDK中的jstat工具 ,也可以在java程序啓動的opt里加上如下幾個參數(注:這兩個參數只針對SUN的HotSpot VM):

   -XX:-PrintGC     Print messages at garbage collection. Manageable.
   -XX:-PrintGC Details     Print more details at garbage collection. Manageable. (Introduced in 1.4.0.)
   -XX:-PrintGCTimeStamps     Print timestamps at garbage collection. Manageable (Introduced in 1.4.0.)

  當把-XX:-PrintGC Details 加入到java opt裏以後可以看見如下輸出:

   [GC [DefNew: 34538K->2311K(36352K), 0.0232439 secs] 45898K->15874K(520320K), 0.0233874 secs]
   [Full GC [Tenured: 13563K->15402K(483968K), 0.2368177 secs] 21163K->15402K(520320K), [Perm : 28671K->28635K(28672K)], 0.2371537 secs]

   他們分別顯示了GC的過程,清理出了多少空間。第一行GC使用的是 ‘普通GC’(Minor Collections),第二行使用的是 ‘全GC’(Major Collections)。他們的區別很大,在第一行最後我們可以看見他的時間是0.0233874秒,而第二行的Full GC的時間是0.2371537秒。第二行的時間是第一行的接近10倍,也就是我們這次調優的重點,減少Full GC 的次數,以爲Full GC 會暫停程序比較長的時間,如果Full GC 的次數比較多。程序就會經常性的假死。當然這只是他們的表面現象,接下來我仔細介紹一下GC,和 Full GC(爲後面的調優做準備)。

     我們知道Java和C++的區別主要是,Java不需要像c++那樣,由程序員主動的釋放內存。而是由JVM裏的GC(Garbage Collection)來,在適當的時候替我們釋放內存。GC 的內部工作,即GC的算法有很多種, 如:標記清除收集器,壓縮收集器,分代收集器等等。現在比較常用的是分代收集(也是SUN VM使用的),即將內存分爲幾個區域,將不同生命週期的對象放在不同區域裏(新的對象會先 生成在Young area,在幾次GC以後,如過沒有收集到,就會逐漸升級到Tenured area)。在GC收集的時候,頻繁收集生命週期短的區域(Young area),因爲這個區域內的對象生命週期比較短,GC 效率也會比較高。而比較少的收集生命週期比較長的區域(Old area or Tenured area),以及基本不收集的永久區(Perm area)。
    注:Young area又分爲三個區域分別叫Eden,和倆個Survivor spaces。Eden用來存放新的對象,Survivor spaces用於 新對象 升級到 Tenured area時的 拷貝。
    我們管收集 生命週期短的區域(Young area) 的收集叫 GC,而管收集 生命週期比較長的區域(Old area or Tenured area)的收集叫 Full GC,因爲他們的收集算法不同,所以使用的時間也會不同。我們要儘量減少 Full GC 的次數。

     接下來介紹一下 HotSpot VM GC 的種類,GC在 HotSpot VM 5.0裏有四種。一種是默認的叫 serial collector,另外幾種分別叫throughput collector,concurrent low pause collector, incremental (sometimes called train) low pause collector(廢棄掉了)。以下是SUN的官方說明:

  1. The throughput collector: this collector uses a parallel version of the young generation collector. It is used if the -XX:+UseParallelGC option is passed on the command line. The tenured generation collector is the same as the serial collector.
  2. The concurrent low pause collector: this collector is used if the -Xincgc or -XX:+UseConcMarkSweepGC is passed on the command line. The concurrent collector is used to collect the tenured generation and does most of the collection concurrently with the execution of the application. The application is paused for short periods during the collection. A parallel version of the young generation copying collector is used with the concurrent collector. The concurrent low pause collector is used if the option -XX:+UseConcMarkSweepGC is passed on the command line.
  3. The incremental (sometimes called train) low pause collector: this collector is used only if -XX:+UseTrainGC is passed on the command line. This collector has not changed since the J2SE Platform version 1.4.2 and is currently not under active development. It will not be supported in future releases. Please see the 1.4.2 GC Tuning Document for information on this collector.

      簡單來說就是throughput collector和concurrent low pause collector:使用多線程的方式,利用多CUP來提高GC的效率,而throughput collector與concurrent low pause collector的去別是throughput collector只在young area使用使用多線程,而concurrent low pause collector則在tenured generation也使用多線程。

       根據官方文檔,他們倆個需要在多CPU的情況下,才能發揮作用。在一個CPU的情況下,會不如默認的serial collector,因爲線程管理需要耗費CPU資源。而在兩個CPU的情況下,也挺高不大。只是在更多CPU的情況下,纔會有所提高。當然 concurrent low pause collector有一種模式可以在CPU較少的機器上,提供儘可能少的停頓的模式,見下文。

       當要使用throughput collector時,在java opt里加上-XX:+UseParallelGC,啓動throughput collector收集。也可加上-XX:ParallelGCThreads=<desired number>來改變線程數。還有兩個參數 -XX:MaxGCPauseMillis=<nnn>和 -XX:GCTimeRatio=<nnn>,MaxGCPauseMillis=<nnn>用來控制最大暫停時間,而-XX: GCTimeRatio可以提高GC說佔CPU的比,以最大話的減小heap。

     當要使用 concurrent low pause collector時,在java的opt里加上 -XX:+UseConcMarkSweepGC。concurrent low pause collector還有一種爲CPU少的機器準備的模式,叫Incremental mode。這種模式使用一個CPU來在程序運行的過程中GC,只用很少的時間暫停程序,檢查對象存活。

       在Incremental mode裏,每個收集過程中,會暫停兩次,第二次略長。第一次用來,簡單從root查詢存活對象。第二次用來,詳細檢查存活對象。整個過程如下:

   * stop all application threads; do the initial mark; resume all application threads(第一次暫停,初始話標記)
   * do the concurrent mark (uses one procesor for the concurrent work)(運行是標記)
   * do the concurrent pre-clean (uses one processor for the concurrent work)(準備清理)
   * stop all application threads; do the remark; resume all application threads(第二次暫停,標記,檢查)
   * do the concurrent sweep (uses one processor for the concurrent work)(運行過程中清理)
   * do the concurrent reset (uses one processor for the concurrent work)(復原)

      當要使用Incremental mode時,需要使用以下幾個變量:
      -XX:+CMSIncrementalMode default: disabled 啓動i-CMS模式(must with -XX:+UseConcMarkSweepGC)
      -XX:+CMSIncrementalPacing default: disabled 提供自動校正功能
      -XX:CMSIncrementalDutyCycle=<N> default: 50 啓動CMS的上線
      -XX:CMSIncrementalDutyCycleMin=<N> default: 10 啓動CMS的下線
      -XX:CMSIncrementalSafetyFactor=<N> default: 10 用來計算循環次數
      -XX:CMSIncrementalOffset=<N> default: 0 最小循環次數(This is the percentage (0-100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections.)
      -XX:CMSExpAvgFactor=<N> default: 25 提供一個指導收集數

     SUN推薦的使用參數是:

       -XX:+UseConcMarkSweepGC \
       -XX:+CMSIncrementalMode \
       -XX:+CMSIncrementalPacing \
       -XX:CMSIncrementalDutyCycleMin=0 \
       -XX:CMSIncrementalDutyCycle=10 \
       -XX:+PrintGC Details \
       -XX:+PrintGCTimeStamps \
       -XX:-TraceClassUnloading

      注:如果使用throughput collector和concurrent low pause collector,這兩種垃圾收集器,需要適當的挺高內存大小,以爲多線程做準備。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章