光說不練假把式,學習Java GC機制的目的是爲了實用,也就是爲了在JVM出現問題時分析緣由並解決之。經過學習,我以爲JVM監控與調優主要的着眼點在於如何配置、如何監控、如何優化3點上。下面就將針對這3點進行學習。
(若是您對Java的內存區域劃分和內存回收機制尚不明確,那在閱讀本文前,請先閱讀個人前一篇博客《Java系列筆記(3) – Java 內存區域和GC機制》,在該博客中,詳細敘述了Java HotSpot虛擬機(Sun/Oracle JDK系列默認的虛擬機)的內存分配和垃圾回收機制。本文不少內容將依據上一篇博客,同時,本文所針對的虛擬機,也是HotSpot虛擬機。)
參數設置php
在Java虛擬機的參數中,有3種表示方法(出自:http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137810.html),用「ps -ef |grep 「java」命令,能夠獲得當前Java進程的全部啓動參數和配置參數:html
- 標準參數(-),全部的JVM實現都必須實現這些參數的功能,並且向後兼容;
- 非標準參數(-X),默認jvm實現這些參數的功能,可是並不保證全部jvm實現都知足,且不保證向後兼容;
- 非Stable參數(-XX),此類參數各個jvm實現會有所不一樣,未來可能會隨時取消,須要慎重使用(可是,這些參數每每是很是有用的);
(額外的,-DpropertyName=「value」的形式定義了一些全局屬性值,下面有介紹。)
本文只重點介紹一些重要和經常使用的參數,若是想了解所有參數,能夠參考下面的文章:java
其實標準參數是用過Java的人都最熟悉的,就是你在運行java命令時後面加上的參數,如java -version, java -jar 等,輸入命令java -help或java -?就能得到當前機器全部java的標準參數列表。
-client
設置jvm使用client模式,這是通常在pc機器上使用的模式,啓動很快,但性能和內存管理效率並不高;多用於桌面應用;web
-server
使用server模式,啓動速度雖然慢(比client模式慢10%左右),可是性能和內存管理效率很高,適用於服務器,用於生成環境、開發環境或測試環境的服務端;
若是沒有指定-server或-client,JVM啓動的時候會自動檢測當前主機是否爲服務器,若是是就以server模式啓動,64位的JVM只有server模式,因此沒法使用-client參數;
默認狀況下,不一樣的啓動模式,執行GC的方式有所區別:算法
啓動模式 | 新生代GC方式 | 舊生代和持久代GC的方式 |
client | 串行 | 串行 |
server | 並行 | 併發 |
若是沒有指定-server或-client模式,則判斷方法以下:
-classpath / -cp
JVM加載和搜索文件的目錄路徑,多個路徑用;分隔。注意,若是使用了-classpath,JVM就不會再搜索環境變量中定義的CLASSPATH路徑。
JVM搜索路徑的順序爲:
1,先搜索JVM自帶的jar或zip包(Bootstrat,搜索路徑能夠用System.getProperty(「sun.boot.class.path」)得到);
2,搜索JRE_HOME/lib/ext下的jar包(Extension,搜索路徑能夠用System.getProperty(「java.ext.dirs」)得到);
3,搜索用戶自定義目錄,順序爲:當前目錄(.),CLASSPATH,-cp;(搜索路徑用System.getProperty(「java.class.path」)得到)apache
-DpropertyName=value
定義系統的全局屬性值,如配置文件地址等,若是value有空格,能夠用-Dname=」space string」這樣的形式來定義,用System.getProperty(「propertyName」)能夠得到這些定義的屬性值,在代碼中也能夠用System.setProperty(「propertyName」,」value」)的形式來定義屬性。windows
-verbose
這是查詢GC問題最經常使用的命令之一,具體參數如:
-verbose:class
輸出jvm載入類的相關信息,當jvm報告說找不到類或者類衝突時可此進行診斷。
-verbose:gc
輸出每次GC的相關狀況,後面會有更詳細的介紹。
-verbose:jni
輸出native方法調用的相關狀況,通常用於診斷jni調用錯誤信息。服務器
非標準參數
非標準參數,是在標準參數的基礎上進行擴展的參數,輸入「java -X」命令,可以得到當前JVM支持的全部非標準參數列表(你會發現,其實並很少哦)。網絡
在不一樣類型的JVM中,採用的參數有所不一樣,
在講解非標準參數時,請參考下面的圖,對內存區域的大小有個形象的瞭解(下圖出自:http://iamzhongyong.iteye.com/blog/1333100):多線程
-Xmn
新生代內存大小的最大值,包括E區和兩個S區的總和,使用方法如:-Xmn65535,-Xmn1024k,-Xmn512m,-Xmn1g (-Xms,-Xmx也是種寫法)
-Xmn只能使用在JDK1.4或以後的版本中,(以前的1.3/1.4版本中,可以使用-XX:NewSize設置年輕代大小,用-XX:MaxNewSize設置年輕代最大值);
若是同時設置了-Xmn和-XX:NewSize,-XX:MaxNewSize,則誰設置在後面,誰就生效;若是同時設置了-XX:NewSize -XX:MaxNewSize與-XX:NewRatio則實際生效的值是:min(MaxNewSize,max(NewSize, heap/(NewRatio+1)))(看考:http://www.open-open.com/home/space.php?uid=71669&do=blog&id=8891)
在開發、測試環境,能夠-XX:NewSize 和 -XX:MaxNewSize來設置新生代大小,但在線上生產環境,使用-Xmn一個便可(推薦),或者將-XX:NewSize 和 -XX:MaxNewSize設置爲同一個值,這樣可以防止在每次GC以後都要調整堆的大小(即:抖動,抖動會嚴重影響性能)
-Xms
初始堆的大小,也是堆大小的最小值,默認值是總共的物理內存/64(且小於1G),默認狀況下,當堆中可用內存小於40%(這個值能夠用-XX: MinHeapFreeRatio 調整,如-X:MinHeapFreeRatio=30)時,堆內存會開始增長,一直增長到-Xmx的大小;
-Xmx
堆的最大值,默認值是總共的物理內存/64(且小於1G),若是Xms和Xmx都不設置,則二者大小會相同,默認狀況下,當堆中可用內存大於70%(這個值能夠用-XX: MaxHeapFreeRatio 調整,如-X:MaxHeapFreeRatio=60)時,堆內存會開始減小,一直減少到-Xms的大小;
整個堆的大小=年輕代大小+年老代大小,堆的大小不包含持久代大小,若是增大了年輕代,年老代相應就會減少,官方默認的配置爲年老代大小/年輕代大小=2/1左右(使用-XX:NewRatio能夠設置-XX:NewRatio=5,表示年老代/年輕代=5/1);
建議在開發測試環境能夠用Xms和Xmx分別設置最小值最大值,可是在線上生產環境,Xms和Xmx設置的值必須同樣,緣由與年輕代同樣——防止抖動;
-Xss
這個參數用於設置每一個線程的棧內存,默認1M,通常來講是不須要改的。除非代碼很少,能夠設置的小點,另一個類似的參數是-XX:ThreadStackSize,這兩個參數在1.6之前,都是誰設置在後面,誰就生效;1.6版本之後,-Xss設置在後面,則以-Xss爲準,-XXThreadStackSize設置在後面,則主線程以-Xss爲準,其它線程以-XX:ThreadStackSize爲準。
-Xrs
減小JVM對操做系統信號(OS Signals)的使用(JDK1.3.1以後纔有效),當此參數被設置以後,jvm將不接收控制檯的控制handler,以防止與在後臺以服務形式運行的JVM衝突(這個用的比較少,參考:http://www.blogjava.net/midstr/archive/2008/09/21/230265.html)。
-Xprof
跟蹤正運行的程序,並將跟蹤數據在標準輸出輸出;適合於開發環境調試。
-Xnoclassgc
關閉針對class的gc功能;由於其阻止內存回收,因此可能會致使OutOfMemoryError錯誤,慎用;
-Xincgc
開啓增量gc(默認爲關閉);這有助於減小長時間GC時應用程序出現的停頓;但因爲可能和應用程序併發執行,因此會下降CPU對應用的處理能力。
-Xloggc:file
與-verbose:gc功能相似,只是將每次GC事件的相關狀況記錄到一個文件中,文件的位置最好在本地,以免網絡的潛在問題。
若與verbose命令同時出如今命令行中,則以-Xloggc爲準。
非Stable參數(非靜態參數)
以-XX表示的非Stable參數,雖然在官方文檔中是不肯定的,不健壯的,各個公司的實現也各有不一樣,但每每很是實用,因此這部分參數對於GC很是重要。JVM(Hotspot)中主要的參數能夠大體分爲3類(參考http://blog.csdn.net/sfdev/article/details/2063928):
- 性能參數( Performance Options):用於JVM的性能調優和內存分配控制,如初始化內存大小的設置;
- 行爲參數(Behavioral Options):用於改變JVM的基礎行爲,如GC的方式和算法的選擇;
- 調試參數(Debugging Options):用於監控、打印、輸出等jvm參數,用於顯示jvm更加詳細的信息;
比較詳細的非Stable參數總結,請參考Java 6 JVM參數選項大全(中文版),
對於非Stable參數,使用方法有4種:
- -XX:+<option> 啓用選項
- -XX:-<option> 不啓用選項
- -XX:<option>=<number> 給選項設置一個數字類型值,可跟單位,例如 32k, 1024m, 2g
- -XX:<option>=<string> 給選項設置一個字符串值,例如-XX:HeapDumpPath=./dump.core
首先介紹性能參數,性能參數每每用來定義內存分配的大小和比例,相比於行爲參數和調試參數,一個比較明顯的區別是性能參數後面每每跟的有數值,經常使用以下:
參數及其默認值 | 描述 |
-XX:NewSize=2.125m
|
新生代對象生成時佔用內存的默認值
|
-XX:MaxNewSize=size | 新生成對象能佔用內存的最大值 |
-XX:MaxPermSize=64m | 方法區所能佔用的最大內存(非堆內存) |
-XX:PermSize=64m | 方法區分配的初始內存 |
-XX:MaxTenuringThreshold=15
|
對象在新生代存活區切換的次數(堅持過MinorGC的次數,每堅持過一次,該值就增長1),大於該值會進入老年代 |
-XX:MaxHeapFreeRatio=70
|
GC後java堆中空閒量佔的最大比例,大於該值,則堆內存會減小
|
-XX:MinHeapFreeRatio=40 | GC後java堆中空閒量佔的最小比例,小於該值,則堆內存會增長 |
-XX:NewRatio=2 | 新生代內存容量與老生代內存容量的比例 |
-XX:ReservedCodeCacheSize= 32m | 保留代碼佔用的內存容量 |
-XX:ThreadStackSize=512 | 設置線程棧大小,若爲0則使用系統默認值 |
-XX:LargePageSizeInBytes=4m |
設置用於Java堆的大頁面尺寸
|
-XX:PretenureSizeThreshold= size | 大於該值的對象直接晉升入老年代(這種對象少用爲好) |
-XX:SurvivorRatio=8 | Eden區域Survivor區的容量比值,如默認值爲8,表明Eden:Survivor1:Survivor2=8:1:1 |
經常使用的行爲參數,主要用來選擇使用什麼樣的垃圾收集器組合,以及控制運行過程當中的GC策略等:
參數及其默認值 | 描述 |
-XX:-UseSerialGC
|
啓用串行GC,即採用
Serial+Serial Old模式
|
-XX:-UseParallelGC
|
啓用並行GC,即採用
Parallel Scavenge+Serial Old收集器組合(-Server模式下的默認組合)
|
-XX:GCTimeRatio=99 | 設置用戶執行時間佔總時間的比例(默認值99,即1%的時間用於GC) |
-XX:MaxGCPauseMillis=time | 設置GC的最大停頓時間(這個參數只對Parallel Scavenge有效) |
-XX:+UseParNewGC | 使用ParNew+Serial Old收集器組合 |
-XX:ParallelGCThreads | 設置執行內存回收的線程數,在+UseParNewGC的狀況下使用 |
-XX:+UseParallelOldGC
|
使用Parallel Scavenge +Parallel Old組合收集器 |
-XX:+UseConcMarkSweepGC | 使用ParNew+CMS+Serial Old組合併發收集,優先使用ParNew+CMS,當用戶線程內存不足時,採用備用方案Serial Old收集。 |
-XX:-DisableExplicitGC | 禁止調用System.gc();但jvm的gc仍然有效 |
-XX:+ScavengeBeforeFullGC | 新生代GC優先於Full GC執行 |
經常使用的調試參數,主要用於監控和打印GC的信息:
參數及其默認值 | 描述 |
-XX:-CITime | 打印消耗在JIT編譯的時間 |
-XX:ErrorFile=./hs_err_pid<pid>.log | 保存錯誤日誌或者數據到文件中 |
-XX:-ExtendedDTraceProbes | 開啓solaris特有的dtrace探針 |
-XX:HeapDumpPath=./java_pid<pid>.hprof | 指定導出堆信息時的路徑或文件名 |
-XX:-HeapDumpOnOutOfMemoryError | 當首次遭遇OOM時導出此時堆中相關信息 |
-XX:OnError=」<cmd args>;<cmd args>」 | 出現致命ERROR以後運行自定義命令 |
-XX:OnOutOfMemoryError=」<cmd args>;<cmd args>」 | 當首次遭遇OOM時執行自定義命令 |
-XX:-PrintClassHistogram | 遇到Ctrl-Break後打印類實例的柱狀信息,與jmap -histo功能相同 |
-XX:-PrintConcurrentLocks | 遇到Ctrl-Break後打印併發鎖的相關信息,與jstack -l功能相同 |
-XX:-PrintCommandLineFlags | 打印在命令行中出現過的標記 |
-XX:-PrintCompilation | 當一個方法被編譯時打印相關信息 |
-XX:-PrintGC | 每次GC時打印相關信息 |
-XX:-PrintGC Details | 每次GC時打印詳細信息 |
-XX:-PrintGCTimeStamps | 打印每次GC的時間戳 |
-XX:-TraceClassLoading | 跟蹤類的加載信息 |
-XX:-TraceClassLoadingPreorder | 跟蹤被引用到的全部類的加載信息 |
-XX:-TraceClassResolution | 跟蹤常量池 |
-XX:-TraceClassUnloading | 跟蹤類的卸載信息 |
-XX:-TraceLoaderConstraints | 跟蹤類加載器約束的相關信息 |
再次聲明,上面的三種參數,主要參考了博客:http://blog.csdn.net/sfdev/article/details/2063928和http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm,後一個比較全面,有興趣的能夠仔細研讀。
這些參數將爲咱們進行GC的監控與調優提供很大助力,是咱們進行GC相關操做的重要工具。
收集器搭配
在介紹了經常使用的配置參數以後,咱們將開始真正的JVM實操征程,首先,咱們要爲應用程序選擇一個合適的垃圾收集器組合,本節請參考《Java系列筆記(3) – Java 內存區域和GC機制》一文中的「垃圾收集器」一節,及上節中的行爲參數。
這裏須要再次引用這幅圖(圖來源於《深刻理解Java虛擬機:JVM高級特效與最佳實現》,圖中兩個收集器之間有連線,說明它們能夠配合使用):
Serial收集器: Serial收集器是在client模式下默認的新生代收集器,其收集效率大約是100M左右的內存須要幾十到100多毫秒;在client模式下,收集桌面應用的內存垃圾,基本上不影響用戶體驗。因此,通常的Java桌面應用中,直接使用Serial收集器(不須要配置參數,用默認便可)。
ParNew收集器:Serial收集器的多線程版本,這種收集器默認開通的線程數與CPU數量相同,-XX:ParallelGCThreads能夠用來設置開通的線程數。
能夠與CMS收集器配合使用,事實上用-XX:+UseConcMarkSweepGC選擇使用CMS收集器時,默認使用的就是ParNew收集器,因此不須要額外設置-XX:+UseParNewGC,設置了也不會衝突,由於會將ParNew+Serial Old做爲一個備選方案;
若是單獨使用-XX:+UseParNewGC參數,則選擇的是ParNew+Serial Old收集器組合收集器。
通常狀況下,在server模式下,若是選擇CMS收集器,則優先選擇ParNew收集器。
Parallel Scavenge收集器:關注的是吞吐量(關於吞吐量的含義見上一篇博客),能夠這麼理解,關注吞吐量,意味着強調任務更快的完成,而如CMS等關注停頓時間短的收集器,強調的是用戶交互體驗。
在須要關注吞吐量的場合,好比數據運算服務器等,就可使用Parallel Scavenge收集器。
老年代收集器以下:
Serial Old收集器:在1.5版本及之前能夠與 Parallel Scavenge結合使用(事實上,也是當時Parallel Scavenge惟一能用的版本),另外就是在使用CMS收集器時的備用方案,發生 Concurrent Mode Failure時使用。
若是是單獨使用,Serial Old通常用在client模式中。
Parallel Old收集器:在1.6版本以後,與 Parallel Scavenge結合使用,以更好的貫徹吞吐量優先的思想,若是是關注吞吐量的服務器,建議使用Parallel Scavenge + Parallel Old 收集器。
CMS收集器:這是當前階段使用很廣的一種收集器,國內不少大的互聯網公司線上服務器都使用這種垃圾收集器(http://blog.csdn.net/wisgood/article/details/17067203),筆者公司的收集器也是這種,CMS收集器以獲取最短回收停頓時間爲目標,很是適合對用戶響應比較高的B/S架構服務器。
CMSIncrementalMode: CMS收集器變種,屬增量式垃圾收集器,在併發標記和併發清理時交替運行垃圾收集器和用戶線程。
G1 收集器:面向服務器端應用的垃圾收集器,計劃將來替代CMS收集器。
- 通常來講,若是是Java桌面應用,建議採用Serial+Serial Old收集器組合,即:-XX:+UseSerialGC(-client下的默認參數)
- 在開發/測試環境,能夠採用默認參數,即採用Parallel Scavenge+Serial Old收集器組合,即:-XX:+UseParallelGC(-server下的默認參數)
- 在線上運算優先的環境,建議採用Parallel Scavenge+Serial Old收集器組合,即:-XX:+UseParallelGC
- 在線上服務響應優先的環境,建議採用ParNew+CMS+Serial Old收集器組合,即:-XX:+UseConcMarkSweepGC
另外在選擇了垃圾收集器組合以後,還要配置一些輔助參數,以保證收集器能夠更好的工做。關於這些參數,請在http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm中查詢其意義和用法,如:
- 選用了ParNew收集器,你可能須要配置4個參數: -XX:SurvivorRatio, -XX:PretenureSizeThreshold, -XX:+HandlePromotionFailure,-XX:MaxTenuringThreshold;
- 選用了 Parallel Scavenge收集器,你可能須要配置3個參數: -XX:MaxGCPauseMillis,-XX:GCTimeRatio, -XX:+UseAdaptiveSizePolicy ;
- 選用了CMS收集器,你可能須要配置3個參數: -XX:CMSInitiatingOccupancyFraction, -XX:+UseCMSCompactAtFullCollection, -XX:CMSFullGCsBeforeCompaction;
關於GC有一個常見的疑問是,在啓動時,個人內存如何分配?通過前面的學習,已經很容易知道,用-Xmn,-Xmx,-Xms,-Xss,-XX:NewSize,-XX:MaxNewSize,-XX:MaxPermSize,-XX:PermSize,-XX:SurvivorRatio,-XX:PretenureSizeThreshold,-XX:MaxTenuringThreshold就基本能夠配置內存啓動時的分配狀況。可是,具體配置多少?設置小了,頻繁GC(甚至內存溢出),設置大了,內存浪費。結合前面對於內存區域和其做用的學習,儘可能考慮以下建議:
- -XX:PermSize儘可能比-XX:MaxPermSize小,-XX:MaxPermSize>= 2 * -XX:PermSize, -XX:PermSize> 64m,通常對於4G內存的機器,-XX:MaxPermSize不會超過256m;
- -Xms = -Xmx(線上Server模式),以防止抖動,大小受操做系統和內存大小限制,若是是32位系統,則通常-Xms設置爲1g-2g(假設有4g內存),在64位系統上,沒有限制,不過通常爲機器最大內存的一半左右;
- -Xmn,在開發環境下,能夠用-XX:NewSize和-XX:MaxNewSize來設置新生代的大小(-XX:NewSize<=-XX:MaxNewSize),在生產環境,建議只設置-Xmn,通常-Xmn的大小是-Xms的1/2左右,不要設置的過大或太小,過大致使老年代變小,頻繁Full GC,太小致使minor GC頻繁。若是不設置-Xmn,能夠採用-XX:NewRatio=2來設置,也是同樣的效果;
- -Xss通常是不須要改的,默認值便可。
- -XX:SurvivorRatio通常設置8-10左右,推薦設置爲10,也即:Survivor區的大小是Eden區的1/10,通常來講,普通的Java程序應用,一次minorGC後,至少98%-99%的對象,都會消亡,因此,survivor區設置爲Eden區的1/10左右,能使Survivor區容納下10-20次的minor GC才滿,而後再進入老年代,這個與 -XX:MaxTenuringThreshold的默認值15次也相匹配的。若是XX:SurvivorRatio設置的過小,會致使原本能經過minor回收掉的對象提早進入老年代,產生沒必要要的full gc;若是XX:SurvivorRatio設置的太大,會致使Eden區相應的被壓縮。
- -XX:MaxTenuringThreshold默認爲15,也就是說,通過15次Survivor輪換(即15次minor GC),就進入老年代, 若是設置的小的話,則年輕代對象在survivor中存活的時間減少,提早進入年老代,對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象在年輕代的存活時間,增長在年輕代即被回收的機率。須要注意的是,設置了 -XX:MaxTenuringThreshold,並不表明着,對象必定在年輕代存活15次才被晉升進入老年代,它只是一個最大值,事實上,存在一個動態計算機制,計算每次晉入老年代的閾值,取閾值和MaxTenuringThreshold中較小的一個爲準。
- -XX:PretenureSizeThreshold通常採用默認值便可。
在JVM運行的過程當中,爲保證其穩定、高效,或在出現GC問題時分析問題緣由,咱們須要對GC進行監控。所謂監控,其實就是分析清楚當前GC的狀況。其目的是鑑別JVM是否在高效的進行垃圾回收,以及有沒有必要進行調優。
經過監控GC,咱們能夠搞清楚不少問題,如:
1,minor GC和full GC的頻率;
2,執行一次GC所消耗的時間;
3,新生代的對象什麼時候被移到老生代以及花費了多少時間;
4,每次GC中,其它線程暫停(Stop the world)的時間;
5,每次GC的效果如何,是否不理想;
………………
監控GC的工具分爲2種:命令行工具和圖形工具;
經常使用的命令行工具備:
注:下面的命令都在JAVA_HOME/bin中,是java自帶的命令。若是您發現沒法使用,請直接進入Java安裝目錄調用或者先設置Java的環境變量,一個簡單的辦法爲:直接運行命令 export PATH=$JAVA_HOME/bin:$PATH;另外,通常的,在Linux下,下面的命令須要sudo權限,在windows下,部分命令的部分選項不能使用。
1,jps
jps命令用於查詢正在運行的JVM進程,經常使用的參數爲:
-q:只輸出LVMID,省略主類的名稱
-m:輸出虛擬機進程啓動時傳給主類main()函數的參數
-l:輸出主類的全類名,若是進程執行的是Jar包,輸出Jar路徑
-v:輸出虛擬機進程啓動時JVM參數
命令格式:jps [option] [hostid]
一個簡單的例子:
在上圖中,有一個vid爲309的apache進程在提供web服務。
2,jstat
jstat能夠實時顯示本地或遠程JVM進程中類裝載、內存、垃圾收集、JIT編譯等數據(若是要顯示遠程JVM信息,須要遠程主機開啓RMI支持)。若是在服務啓動時沒有指定啓動參數-verbose:gc,則能夠用jstat實時查看gc狀況。
jstat有以下選項:
-class:監視類裝載、卸載數量、總空間及類裝載所耗費的時間
-gc:監聽Java堆情況,包括Eden區、兩個Survivor區、老年代、永久代等的容量,以用空間、GC時間合計等信息
-gccapacity:監視內容與-gc基本相同,但輸出主要關注java堆各個區域使用到的最大和最小空間
-gcutil:監視內容與-gc基本相同,但輸出主要關注已使用空間佔總空間的百分比
-gccause:與-gcutil功能同樣,可是會額外輸出致使上一次GC產生的緣由
-gcnew:監視新生代GC情況
-gcnewcapacity:監視內同與-gcnew基本相同,輸出主要關注使用到的最大和最小空間
-gcold:監視老年代GC狀況
-gcoldcapacity:監視內同與-gcold基本相同,輸出主要關注使用到的最大和最小空間
-gcpermcapacity:輸出永久代使用到最大和最小空間
-compiler:輸出JIT編譯器編譯過的方法、耗時等信息
-printcompilation:輸出已經被JIT編譯的方法
命令格式:jstat [option vmid [interval[s|ms] [count]]]
jstat能夠監控遠程機器,命令格式中VMID和LVMID特別說明:若是是本地虛擬機進程,VMID和LVMID是一致的,若是是遠程虛擬機進程,那麼VMID格式是: [protocol:][//]lvmid[@hostname[:port]/servername],若是省略interval和count,則只查詢一次
查看gc狀況的例子:
在圖中,命令sudo jstat -gc 309 1000 5表明着:蒐集vid爲309的java進程的總體gc狀態, 每1000ms收集一次,共收集5次;XXXC表示該區容量,XXXU表示該區使用量,各列解釋以下:
S0C:S0區容量(S1區相同,略)
S0U:S0區已使用
EC:E區容量
EU:E區已使用
OC:老年代容量
OU:老年代已使用
PC:Perm容量
PU:Perm區已使用
YGC:Young GC(Minor GC)次數
YGCT:Young GC總耗時
FGC:Full GC次數
FGCT:Full GC總耗時
GCT:GC總耗時
用gcutil查看內存的例子:
圖中的各列與用gc參數時基本一致,不一樣的是這裏顯示的是已佔用的百分比,如S0爲86.53,表明着S0區已使用了86.53%
3,jinfo
用於查詢當前運行這的JVM屬性和參數的值。
jinfo可使用以下選項:
-flag:顯示未被顯示指定的參數的系統默認值
-flag [+|-]name或-flag name=value: 修改部分參數
-sysprops:打印虛擬機進程的System.getProperties()
命令格式:jinfo [option] pid
4,jmap
用於顯示當前Java堆和永久代的詳細信息(如當前使用的收集器,當前的空間使用率等)
-dump:生成java堆轉儲快照
-heap:顯示java堆詳細信息(只在Linux/Solaris下有效)
-F:當虛擬機進程對-dump選項沒有響應時,可以使用這個選項強制生成dump快照(只在Linux/Solaris下有效)
-finalizerinfo:顯示在F-Queue中等待Finalizer線程執行finalize方法的對象(只在Linux/Solaris下有效)
-histo:顯示堆中對象統計信息
-permstat:以ClassLoader爲統計口徑顯示永久代內存狀態(只在Linux/Solaris下有效)
命令格式:jmap [option] vmid
其中前面3個參數最重要,如:
查看對詳細信息:sudo jmap -heap 309
生成dump文件: sudo jmap -dump:file=./test.prof 309
部分用戶沒有權限時,採用admin用戶:sudo -u admin -H jmap -dump:format=b,file=文件名.hprof pid
查看當前堆中對象統計信息:sudo jmap -histo 309:該命令顯示3列,分別爲對象數量,對象大小,對象名稱,經過該命令能夠查看是否內存中有大對象;
有的用戶可能沒有jmap權限:sudo -u admin -H jmap -histo 309 | less
5,jhat
用於分析使用jmap生成的dump文件,是JDK自帶的工具,使用方法爲: jhat -J -Xmx512m [file]
不過jhat沒有mat好用,推薦使用mat(Eclipse插件: http://www.eclipse.org/mat ),mat速度更快,並且是圖形界面。
6,jstack
用於生成當前JVM的全部線程快照,線程快照是虛擬機每一條線程正在執行的方法,目的是定位線程出現長時間停頓的緣由。
-F:當正常輸出的請求不被響應時,強制輸出線程堆棧
-l:除堆棧外,顯示關於鎖的附加信息
-m:若是調用到本地方法的話,能夠顯示C/C++的堆棧
命令格式:jstack [option] vmid
7,-verbosegc
-verbosegc是一個比較重要的啓動參數,記錄每次gc的日誌,下面的表格對比了jstat和-verbosegc:
jstat
|
-verbosegc
|
|
監控對象
|
運行在本機的Java應用能夠把日誌輸出到終端上,或者藉助jstatd命令經過網絡鏈接遠程的Java應用。
|
只有那些把-verbogc做爲啓動參數的JVM。
|
輸出信息
|
堆狀態(已用空間,最大限制,GC執行次數/時間,等等)
|
執行GC先後新生代和老年代空間大小,GC執行時間。
|
輸出時間
|
Every designated time
每次設定好的時間。 |
每次GC發生的時候。
|
用途 |
觀察堆空間變化狀況
|
瞭解單次GC產生的效果。
|
與-verbosegc配合使用的一些經常使用參數爲:
-XX:+PrintGCDetails,打印GC信息,這是-verbosegc默認開啓的選項
-XX:+PrintGCTimeStamps,打印每次GC的時間戳
-XX:+PrintHeapAtGC:每次GC時,打印堆信息
-XX:+PrintGCDateStamps (from JDK 6 update 4) :打印GC日期,適合於長期運行的服務器
-Xloggc:/home/admin/logs/gc.log:制定打印信息的記錄的日誌位置
每條verbosegc打印出的gc日誌,都相似於下面的格式:
time [GC [<collector>: <starting occupancy1> -> <ending occupancy1>(total occupancy1), <pause time1> secs] <starting occupancy3> -> <ending occupancy3>(total occupancy3), <pause time3> secs]
如:
這些選項的意義是:
time:執行GC的時間,須要添加-XX:+PrintGCDateStamps參數纔有;
collector:minor gc使用的收集器的名字。
starting occupancy1:GC執行前新生代空間大小。
ending occupancy1:GC執行後新生代空間大小。
total occupancy1:新生代總大小
pause time1:由於執行minor GC,Java應用暫停的時間。
starting occupancy3:GC執行前堆區域總大小
ending occupancy3:GC執行後堆區域總大小
total occupancy3:堆區總大小
pause time3:Java應用因爲執行堆空間GC(包括full GC)而中止的時間。
8,可視化工具
監控和分析GC也有一些可視化工具,比較常見的有JConsole和VisualVM,有興趣的能夠看看下面的文章,在此再也不贅述:
http://blog.csdn.net/java2000_wl/article/details/8049707
一切都是爲了這一步,調優,在調優以前,咱們須要記住下面的原則:
- 多數的Java應用不須要在服務器上進行GC優化;
- 多數致使GC問題的Java應用,都不是由於咱們參數設置錯誤,而是代碼問題;
- 在應用上線以前,先考慮將機器的JVM參數設置到最優(最適合);
- 減小建立對象的數量;
- 減小使用全局變量和大對象;
- GC優化是到最後不得已才採用的手段;
- 在實際使用中,分析GC狀況優化代碼比優化GC參數要多得多;
GC優化的目的有兩個(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml):
- 將轉移到老年代的對象數量下降到最小;
- 減小full GC的執行時間;
爲了達到上面的目的,通常地,你須要作的事情有:
- 減小使用全局變量和大對象;
- 調整新生代的大小到最合適;
- 設置老年代的大小爲最合適;
- 選擇合適的GC收集器;
在上面的4條方法中,用了幾個「合適」,那究竟什麼纔算合適,通常的,請參考上面「收集器搭配」和「啓動內存分配」兩節中的建議。但這些建議不是萬能的,須要根據您的機器和應用狀況進行發展和變化,實際操做中,能夠將兩臺機器分別設置成不一樣的GC參數,而且進行對比,選用那些確實提升了性能或減小了GC時間的參數。
真正熟練的使用GC調優,是創建在屢次進行GC監控和調優的實戰經驗上的,進行監控和調優的通常步驟爲:
1,監控GC的狀態
使用各類JVM工具,查看當前日誌,分析當前JVM參數設置,而且分析當前堆內存快照和gc日誌,根據實際的各區域內存劃分和GC執行時間,以爲是否進行優化;
2,分析結果,判斷是否須要優化
若是各項參數設置合理,系統沒有超時日誌出現,GC頻率不高,GC耗時不高,那麼沒有必要進行GC優化;若是GC時間超過1-3秒,或者頻繁GC,則必須優化;
注:若是知足下面的指標,則通常不須要進行GC:
- Minor GC執行時間不到50ms;
- Minor GC執行不頻繁,約10秒一次;
- Full GC執行時間不到1s;
- Full GC執行頻率不算頻繁,不低於10分鐘1次;
3,調整GC類型和內存分配
若是內存分配過大或太小,或者採用的GC收集器比較慢,則應該優先調整這些參數,而且先找1臺或幾臺機器進行beta,而後比較優化過的機器和沒有優化的機器的性能對比,並有針對性的作出最後選擇;
4,不斷的分析和調整
經過不斷的試驗和試錯,分析並找到最合適的參數
5,全面應用參數
若是找到了最合適的參數,則將這些參數應用到全部服務器,並進行後續跟蹤。
上面的內容都是紙上談兵,下面咱們以一些真實例子來進行說明:
實例1:
筆者昨日發現部分開發測試機器出現異常:java.lang.OutOfMemoryError: GC overhead limit exceeded,這個異常表明:GC爲了釋放很小的空間卻耗費了太多的時間,其緣由通常有兩個:1,堆過小,2,有死循環或大對象;
筆者首先排除了第2個緣由,由於這個應用同時是在線上運行的,若是有問題,早就掛了。因此懷疑是這臺機器中堆設置過小;
使用ps -ef |grep 「java」查看,發現:
該應用的堆區設置只有768m,而機器內存有2g,機器上只跑這一個java應用,沒有其餘須要佔用內存的地方。另外,這個應用比較大,須要佔用的內存也比較多;
筆者經過上面的狀況判斷,只須要改變堆中各區域的大小設置便可,因而改爲下面的狀況:
跟蹤運行狀況發現,相關異常沒有再出現;
實例2:(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)
一個服務系統,常常出現卡頓,分析緣由,發現Full GC時間太長:
jstat -gcutil:
S0 S1 E O P YGC YGCT FGC FGCT GCT
12.16 0.00 5.18 63.78 20.32 54 2.047 5 6.946 8.993
分析上面的數據,發現Young GC執行了54次,耗時2.047秒,每次Young GC耗時37ms,在正常範圍,而Full GC執行了5次,耗時6.946秒,每次平均1.389s,數據顯示出來的問題是:Full GC耗時較長,分析該系統的是指發現,NewRatio=9,也就是說,新生代和老生代大小之比爲1:9,這就是問題的緣由:
1,新生代過小,致使對象提早進入老年代,觸發老年代發生Full GC;
2,老年代較大,進行Full GC時耗時較大;
優化的方法是調整NewRatio的值,調整到4,發現Full GC沒有再發生,只有Young GC在執行。這就是把對象控制在新生代就清理掉,沒有進入老年代(這種作法對一些應用是頗有用的,但並非對全部應用都要這麼作)
實例3:
一應用在性能測試過程當中,發現內存佔用率很高,Full GC頻繁,使用sudo -u admin -H jmap -dump:format=b,file=文件名.hprof pid 來dump內存,生成dump文件,並使用Eclipse下的mat差距進行分析,發現:
從圖中能夠看出,這個線程存在問題,隊列LinkedBlockingQueue所引用的大量對象並未釋放,致使整個線程佔用內存高達378m,此時通知開發人員進行代碼優化,將相關對象釋放掉便可。
說明
本文是Java系列筆記的第4篇,這篇文章寫了近3個月,一方面是這部分對我來講也是學習階段,另外一方面是這段時間一直在作項目,直到最近才比較有時間。
本人能力有限,若是有錯漏,請留言指正。
參考資料
《深刻理解Java虛擬機:JVM高級特效與最佳實現》JVM啓動參數大全, http://www.blogjava.net/midstr/archive/2008/09/21/230265.htmlJVM系列三:JVM參數設置、分析, http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.htmlJava 6 JVM參數選項大全(中文版), http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm成爲JavaGC專家Part II — 如何監控Java垃圾回收機制, http://www.importnew.com/2057.html成爲Java GC專家系列(3) — 如何優化Java垃圾回收機制, http://www.importnew.com/3146.htmlJDK5.0垃圾收集優化之–Don’t Pause, http://calvin.iteye.com/blog/91905Java HOTSPOT VM參數大全, http://tech.sina.com.cn/s/2009-09-23/09561077572.shtml【原】GC的默認方式, http://iamzhongyong.iteye.com/blog/1447314JAVA啓動參數大全之三:非Stable參數, http://blog.csdn.net/sfdev/article/details/2063928Java虛擬機學習 – 內存調優, http://blog.csdn.net/java2000_wl/article/details/8090940內存溢出, http://www.open-open.com/home/space.php?uid=71669&do=blog&id=8891如何查看JVM的擴展參數:-X, http://www.blogjava.net/beansoft/archive/2012/03/01/371088.htmlJVM內存情況查看方法和分析工具, http://hi.baidu.com/kingfly666666/item/e710a4371c60b0f1e7bb7a32虛擬機學習系列 – 附 – 虛擬機參數, http://blog.csdn.net/su1216/article/details/7780924JVM系列四:生產環境參數實例及分析【生產環境實例增長中】, http://www.cnblogs.com/redcreen/archive/2011/05/05/2038331.html垃圾收集器與內存分配策略, http://raging-sweet.iteye.com/blog/1170198JVM垃圾收集器使用調查:CMS最受歡迎 , http://blog.csdn.net/wisgood/article/details/17067203Xms Xmx PermSize MaxPermSize 區別, http://www.cnblogs.com/mingforyou/archive/2012/03/03/2378143.htmlJava虛擬機學習 – JDK可視化監控工具, http://blog.csdn.net/java2000_wl/article/details/8049707虛擬機學習系列 – 6 – JDK工具, http://blog.csdn.net/su1216/article/details/7780857JVM監控工具介紹jstack, jconsole, jinfo, jmap, jdb, jstat, http://hi.baidu.com/lotusxyhf/item/9cd8fcb8d6f8c1a5ebba935bJVM 與 jstat, http://blog.sina.com.cn/s/blog_56fcfd620100hdcp.html