天天看點

JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)

原文:(PS:看到字型顔色比較淺,做了下調整後轉載)

  • http://hi.baidu.com/i1see1you/item/295c1dc81f91ab55bdef69e5
  • gc日志分析工具: http://qa.blog.163.com/blog/static/19014700220128199421589/
  • Java GC 日志圖解: http://www.chinasb.org/archives/2012/09/4921.shtml

概述

java的最大好處是自動垃圾回收,這樣就無需我們手動的釋放對象空間了,但是也産生了相應的負效果,gc是需要時間和資源的,不好的gc會嚴重影響系統的系能,是以良好的gc是JVM的高性能的保證。JVM堆分為新生代,舊生代和年老代,新生代可用的gc方式有:串行gc(Serial Copying),并行回收gc(Parellel Scavenge),并行gc(ParNew),舊生代和年老代可用的gc方式有串行gc(Serial MSC),并行gc(Parallel MSC),并發gc(CMS)。

回收方式的選擇

jvm有client和server兩種模式,這兩種模式的gc預設方式是不同的:

client模式下,新生代選擇的是串行gc,舊生代選擇的是串行gc

server模式下,新生代選擇的是并行回收gc,舊生代選擇的是并行gc

一般來說我們系統應用選擇有兩種方式:吞吐量優先和暫停時間優先,對于吞吐量優先的采用server預設的并行gc方式,對于暫停時間優先的選用并發gc(CMS)方式。

CMS gc

CMS,全稱Concurrent Low Pause Collector,是jdk1.4後期版本開始引入的新gc算法,在jdk5和jdk6中得到了進一步改進,它的主要适合場景是對響應時間的重要性需求大于對吞吐量的要求,能夠承受垃圾回收線程和應用線程共享處理器資源,并且應用中存在比較多的長生命周期的對象的應用。CMS是用于對tenured generation的回收,也就是年老代的回收,目标是盡量減少應用的暫停時間,減少full gc發生的幾率,利用和應用程式線程并發的垃圾回收線程來标記清除年老代。在我們的應用中,因為有緩存的存在,并且對于響應時間也有比較高的要求,是以希望能嘗試使用CMS來替代預設的server型JVM使用的并行收集器,以便獲得更短的垃圾回收的暫停時間,提高程式的響應性。

CMS并非沒有暫停,而是用兩次短暫停來替代串行标記整理算法的長暫停,它的收集周期是這樣:

初始标記(CMS-initial-mark) -> 并發标記(CMS-concurrent-mark) -> 重新标記(CMS-remark) -> 并發清除(CMS-concurrent-sweep) ->并發重設狀态等待下次CMS的觸發(CMS-concurrent-reset)。

其中的1,3兩個步驟需要暫停所有的應用程式線程的。第一次暫停從root對象開始标記存活的對象,這個階段稱為初始标記;第二次暫停是在并發标記之後,暫停所有應用程式線程,重新标記并發标記階段遺漏的對象(在并發标記階段結束後對象狀态的更新導緻)。第一次暫停會比較短,第二次暫停通常會比較長,并且 remark這個階段可以并行标記。

而并發标記、并發清除、并發重設階段的所謂并發,是指一個或者多個垃圾回收線程和應用程式線程并發地運作,垃圾回收線程不會暫停應用程式的執行,如果你有多于一個處理器,那麼并發收集線程将與應用線程在不同的處理器上運作,顯然,這樣的開銷就是會降低應用的吞吐量。Remark階段的并行,是指暫停了所有應用程式後,啟動一定數目的垃圾回收程序進行并行标記,此時的應用線程是暫停的。

full gc

full gc是對新生代,舊生代,以及持久代的統一回收,由于是對整個空間的回收,是以比較慢,系統中應當盡量減少full gc的次數。

如下幾種情況下會發生full gc:

  1. 舊生代空間不足
  2. 持久代空間不足
  3. CMS GC時出現了promotion failed和concurrent mode failure
  4. 統計得到新生代minor gc時晉升到舊生代的平均大小小于舊生代剩餘空間
  5. 直接調用System.gc,可以DisableExplicitGC來禁止
  6. 存在rmi調用時,預設會每分鐘執行一次System.gc,可以通過-Dsun.rmi.dgc.server.gcInterval=3600000來設定大點的間隔。

Gc日志參數

通過在tomcat啟動腳本中添加相關參數生成gc日志

  • -verbose.gc

    開關可顯示GC的操作内容。打開它,可以顯示最忙和最空閑收集行為發生的時間、收集前後的記憶體大小、收集需要的時間等。
  • 打開

    -xx:+printGCdetails

    開關,可以詳細了解GC中的變化。
  • 打開

    -XX:+PrintGCTimeStamps

    開關,可以了解這些垃圾收集發生的時間,自JVM啟動以後以秒計量。
  • 最後,通過

    -xx:+PrintHeapAtGC

    開關了解堆的更詳細的資訊。
  • 為了了解新域的情況,可以通過

    -XX:+PrintTenuringDistribution

    開關了解獲得使用期的對象權。
  • -Xloggc:$CATALINA_BASE/logs/gc.log

     gc日志産生的路徑
  • -XX:+PrintGCApplicationStoppedTime

     輸出GC造成應用暫停的時間
  • -XX:+PrintGCDateStamps

     GC發生的時間資訊

Opentsdb打開Gc參數

1

2

3

4

5

6

7

8

9

10

11

12

# tsdb.local

# http://opentsdb.net/docs/build/html/user_guide/cli/index.html

GCARGS="-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps\

-XX:+PrintTenuringDistribution -Xloggc:

/tmp/tsd-gc-

`

date

+%s`.log"

if

test

-t 0; 

then

# if stdin is a tty, don't turn on GC logging.

GCARGS=

fi

# The Sun JDK caches all name resolution results forever, which is stupid.

# This forces you to restart your application if any of the backends change

# IP. Instead tell it to cache names for only 10 minutes at most.

FIX_DNS=

'-Dsun.net.inetaddr.ttl=600'

JVMARGS=

"$JVMARGS $GCARGS $FIX_DNS"

常用JVM參數

分析gc日志後,經常需要調整jvm記憶體相關參數,常用參數如下

  • -Xms:初始堆大小,預設為實體記憶體的1/64(<1GB);預設(MinHeapFreeRatio參數可以調整)空餘堆記憶體小于40%時,JVM就會增大堆直到-Xmx的最大限制
  • -Xmx:最大堆大小,預設(MaxHeapFreeRatio參數可以調整)空餘堆記憶體大于70%時,JVM會減少堆直到 -Xms的最小限制
  • -Xmn:新生代的記憶體空間大小,注意:此處的大小是(eden+ 2 survivor space)。與jmap -heap中顯示的New gen是不同的。整個堆大小=新生代大小 + 老生代大小 + 永久代大小。在保證堆大小不變的情況下,增大新生代後,将會減小老生代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。
  • -XX:SurvivorRatio:新生代中Eden區域與Survivor區域的容量比值,預設值為8。兩個Survivor區與一個Eden區的比值為2:8,一個Survivor區占整個年輕代的1/10。
  • -Xss:每個線程的堆棧大小。JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。應根據應用的線程所需記憶體大小進行适當調整。在相同實體記憶體下,減小這個值能生成更多的線程。但是作業系統對一個程序内的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。一般小的應用, 如果棧不是很深, 應該是128k夠用的,大的應用建議使用256k。這個選項對性能影響比較大,需要嚴格的測試。和threadstacksize選項解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話:"-Xss is translated in a VM flag named ThreadStackSize”一般設定這個值就可以了。
  • -XX:PermSize:設定永久代(perm gen)初始值。預設值為實體記憶體的1/64。
  • -XX:MaxPermSize:設定持久代最大值。實體記憶體的1/4。

示例

下面對如下的參數進行分析:

JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4
-verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log 
-Djava.awt.headless=true 
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000
-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15"
      
  • -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m

Xms,即為jvm啟動時得JVM初始堆大小,Xmx為jvm的最大堆大小,xmn為新生代的大小,permsize為永久代的初始大小,MaxPermSize為永久代的最大空間。

  • -XX:SurvivorRatio=4

SurvivorRatio為新生代空間中的Eden區和救助空間Survivor區的大小比值,預設是32,也就是說Eden區是 Survivor區的32倍大小,要注意Survivo是有兩個區的,是以Surivivor其實占整個young genertation的1/34。調小這個參數将增大survivor區,讓對象盡量在survitor區呆長一點,減少進入年老代的對象。去掉救助空間的想法是讓大部分不能馬上回收的資料盡快進入年老代,加快年老代的回收頻率,減少年老代暴漲的可能性,這個是通過将-XX:SurvivorRatio 設定成比較大的值(比如65536)來做到。

  • -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log

将虛拟機每次垃圾回收的資訊寫到日志檔案中,檔案名由file指定,檔案格式是平檔案,内容和-verbose:gc輸出内容相同。

  • -Djava.awt.headless=true

Headless模式是系統的一種配置模式。在該模式下,系統缺少了顯示裝置、鍵盤或滑鼠。

  • -XX:+PrintGCTimeStamps -XX:+PrintGCDetails

設定gc日志的格式

  • -Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000

指定rmi調用時gc的時間間隔

  • -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15

采用并發gc方式,經過15次minor gc 後進入年老代

  • Xms 是指設定程式啟動時占用記憶體大小。一般來講,大點,程式會啟動的快一點,但是也可能會導緻機器暫時間變慢。
    
    Xmx 是指設定程式運作期間最大可占用的記憶體大小。如果程式運作需要占用更多的記憶體,超出了這個設定值,就會抛出OutOfMemory異常。
    
    Xss 是指設定每個線程的堆棧大小。這個就要依據你的程式,看一個線程大約需要占用多少記憶體,可能會有多少線程同時運作等。
    
    以上三個參數的設定都是預設以Byte為機關的,也可以在數字後面添加[k/K]或者[m/M]來表示KB或者MB。而且,超過機器本身的記憶體大小也是不可以的,否則就等着機器變慢而不是程式變慢了。
    
    -Xmsn
    Specify the initial size, in bytes, of the memory allocation pool. This value must be a multiple of 1024 greater than 1MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is chosen at runtime based on system configuration. For more information, see HotSpot Ergonomics
    Examples:
           -Xms6291456
           -Xms6144k
           -Xms6m
          
    -Xmxn
    Specify the maximum size, in bytes, of the memory allocation pool. This value must a multiple of 1024 greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is chosen at runtime based on system configuration. For more information, see HotSpot Ergonomics
    Examples:
           -Xmx83886080
           -Xmx81920k
           -Xmx80m
    
    -Xssn
    Set thread stack size. 
          

一些常見問題

  • 為了避免Perm區滿引起的full gc,建議開啟CMS回收Perm區選項:
    +CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
          
  • 預設CMS是在tenured generation沾滿68%的時候開始進行CMS收集,如果你的年老代增長不是那麼快,并且希望降低CMS次數的話,可以适當調高此值:
    -XX:CMSInitiatingOccupancyFraction=80
          
  • 遇到兩種fail引起full gc:Prommotion failed和Concurrent mode failed時:

Prommotion failed的日志輸出大概是這樣:

[ParNew (promotion failed): 320138K->320138K(353920K), 0.2365970 secs]42576.951: [CMS: 1139969K->1120688K( 166784K), 9.2214860 secs] 1458785K->1120688K(2520704K), 9.4584090 secs]

這個問題的産生是由于救助空間不夠,進而向年老代轉移對象,年老代沒有足夠的空間來容納這些對象,導緻一次full gc的産生。解決這個問題的辦法有兩種完全相反的傾向:增大救助空間、增大年老代或者去掉救助空間。

Concurrent mode failed的日志大概是這樣的:

(concurrent mode failure): 1228795K->1228598K(1228800K), 7.6748280 secs] 1911483K->1681165K(1911488K), [CMS Perm : 225407K->225394K(262144K)], 7.6751800 secs]

問題的産生原因是由于CMS回收年老代的速度太慢,導緻年老代在CMS完成前就被沾滿,引起full gc,避免這個現象的産生就是調小-XX:CMSInitiatingOccupancyFraction參數的值,讓CMS更早更頻繁的觸發,降低年老代被沾滿的可能。

Gc日志分析工具

GCHisto

http://java.net/projects/gchisto

直接點選gchisto.jar就可以運作,點add載入gc.log

統計了總共gc次數,youngGC次數,FullGC次數,次數的百分比,GC消耗的時間,百分比,平均消耗時間,消耗時間最小最大值等

  • 統計的圖形化表示
JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)
  • YoungGC,FullGC不同消耗時間上次數的分布圖,勾選可以顯示youngGC或fullGC單獨的分布情況
JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)
  • 整個時間過程詳細的gc情況,可以對整個過程進行剖析
JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)

GCLogViewer

http://code.google.com/p/gclogviewer/

點選run.bat運作

整個過程gc情況的趨勢圖,還顯示了gc類型,吞吐量,平均gc頻率,記憶體變化趨勢等

Tools裡還能比較不同gc日志:

JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)

HPjmeter

  • 擷取位址 http://www.hp.com/go/java
  • 參考文檔 http://www.javaperformancetuning.com/tools/hpjtune/index.shtml

工具很強大,但隻能打開由以下參數生成的GC log, -verbose:gc -Xloggc:gc.log,添加其他參數生成的gc.log無法打開。

GCViewer

http://www.tagtraum.com/gcviewer.html

這個工具用的挺多的,但隻能在JDK1.5以下的版本中運作,1.6以後沒有對應。

garbagecat

http://code.google.com/a/eclipselabs.org/p/garbagecat/wiki/Documentation

其它監控方法

  • Jvisualvm

Jvisualvm動态分析jvm記憶體情況和gc情況,插件:visualGC

JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)
JVM gc參數設定與分析原文:(PS:看到字型顔色比較淺,做了下調整後轉載)

jvisualvm還可以heapdump出對應hprof檔案(預設存放路徑:監控的伺服器 /tmp下),利用相關工具,比如HPjmeter可以對其進行分析

grep Full gc.log粗略觀察FullGC發生頻率

jstat –gcutil [pid] [intervel] [count]
      
  • jmap

jmap -histo pid可以觀測對象的個數和占用空間

jmap -heap pid可以觀測jvm配置參數,堆記憶體各區使用情況

  • jprofiler,jmap dump出來用MAT分析

如果要分析的dump檔案很大的話,就需要很多記憶體,很容易crash。

是以在啟動時,我們應該加上一些參數: Java –Xms512M –Xmx1024M –Xss8M

參考資料:

探秘Java虛拟機——記憶體管理與垃圾回收http://sunbean.blog.51cto.com/972509/768034