Java系列筆記(4) - JVM監控與調優【轉】

Java系列筆記(4) - JVM監控與調優【轉】

目錄

參數設置
收集器搭配
啟動內存分配
監控工具和方法
調優方法
調優實例
??? ?
光說不練假把式,學習Java GC機制的目的是為了實用,也就是為了在JVM出現問題時分析原因并解決之。通過學習,我覺得JVM監控與調優主要的著眼點在于如何配置、如何監控、如何優化3點上。下面就將針對這3點進行學習。
?????(如果您對Java的內存區域劃分和內存回收機制尚不明確,那在閱讀本文前,請先閱讀我的前一篇博客《Java系列筆記(3) - Java 內存區域和GC機制》,在該博客中,詳細敘述了Java HotSpot虛擬機(Sun/Oracle JDK系列默認的虛擬機)的內存分配和垃圾回收機制。本文很多內容將依據上一篇博客,同時,本文所針對的虛擬機,也是HotSpot虛擬機。)
參數設置


?在Java虛擬機的參數中,有3種表示方法(出自:http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137810.html),用“ps -ef |grep "java"命令,可以得到當前Java進程的所有啟動參數和配置參數:

  • 標準參數(-),所有的JVM實現都必須實現這些參數的功能,而且向后兼容;
  • 非標準參數(-X),默認jvm實現這些參數的功能,但是并不保證所有jvm實現都滿足,且不保證向后兼容;
  • 非Stable參數(-XX),此類參數各個jvm實現會有所不同,將來可能會隨時取消,需要慎重使用(但是,這些參數往往是非常有用的);

(額外的,-DpropertyName=“value”的形式定義了一些全局屬性值,下面有介紹。)
本文只重點介紹一些重要和常用的參數,如果想了解全部參數,可以參考下面的文章:

《Java HotSpot VM Options》
《Java 6 JVM參數選項大全(中文版)》(上面一篇的中文版)
《JVM啟動參數大全》
標準參數

其實標準參數是用過Java的人都最熟悉的,就是你在運行java命令時后面加上的參數,如java -version, java -jar 等,輸入命令java -help或java -?就能獲得當前機器所有java的標準參數列表。
-client
設置jvm使用client模式,這是一般在pc機器上使用的模式,啟動很快,但性能和內存管理效率并不高;多用于桌面應用;

-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")獲得)

-DpropertyName=value
定義系統的全局屬性值,如配置文件地址等,如果value有空格,可以用-Dname="space string"這樣的形式來定義,用System.getProperty("propertyName")可以獲得這些定義的屬性值,在代碼中也可以用System.setProperty("propertyName","value")的形式來定義屬性。

-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=40GC后java堆中空閑量占的最小比例,小于該值,則堆內存會增加
-XX:NewRatio=2新生代內存容量與老生代內存容量的比例
-XX:ReservedCodeCacheSize= 32m保留代碼占用的內存容量
-XX:ThreadStackSize=512設置線程棧大小,若為0則使用系統默認值
-XX:LargePageSizeInBytes=4m
設置用于Java堆的大頁面尺寸
-XX:PretenureSizeThreshold= size ??大于該值的對象直接晉升入老年代(這種對象少用為好)
-XX:SurvivorRatio=8Eden區域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(甚至內存溢出),設置大了,內存浪費。結合前面對于內存區域和其作用的學習,盡量考慮如下建議:

  1. -XX:PermSize盡量比-XX:MaxPermSize小,-XX:MaxPermSize>= 2 * -XX:PermSize, -XX:PermSize> 64m,一般對于4G內存的機器,-XX:MaxPermSize不會超過256m;
  2. -Xms =? -Xmx(線上Server模式),以防止抖動,大小受操作系統和內存大小限制,如果是32位系統,則一般-Xms設置為1g-2g(假設有4g內存),在64位系統上,沒有限制,不過一般為機器最大內存的一半左右;
  3. -Xmn,在開發環境下,可以用-XX:NewSize和-XX:MaxNewSize來設置新生代的大小(-XX:NewSize<=-XX:MaxNewSize),在生產環境,建議只設置-Xmn,一般-Xmn的大小是-Xms的1/2左右,不要設置的過大或過小,過大導致老年代變小,頻繁Full GC,過小導致minor GC頻繁。如果不設置-Xmn,可以采用-XX:NewRatio=2來設置,也是一樣的效果;
  4. -Xss一般是不需要改的,默認值即可。
  5. -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區相應的被壓縮。
  6. -XX:MaxTenuringThreshold默認為15,也就是說,經過15次Survivor輪換(即15次minor GC),就進入老年代, 如果設置的小的話,則年輕代對象在survivor中存活的時間減小,提前進入年老代,對于年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象在年輕代的存活時間,增加在年輕代即被回收的概率。需要注意的是,設置了 -XX:MaxTenuringThreshold,并不代表著,對象一定在年輕代存活15次才被晉升進入老年代,它只是一個最大值,事實上,存在一個動態計算機制,計算每次晉入老年代的閾值,取閾值和MaxTenuringThreshold中較小的一個為準。
  7. -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

調優方法


一切都是為了這一步,調優,在調優之前,我們需要記住下面的原則:

  1. 多數的Java應用不需要在服務器上進行GC優化;
  2. 多數導致GC問題的Java應用,都不是因為我們參數設置錯誤,而是代碼問題;
  3. 在應用上線之前,先考慮將機器的JVM參數設置到最優(最適合);
  4. 減少創建對象的數量;
  5. 減少使用全局變量和大對象;
  6. GC優化是到最后不得已才采用的手段;
  7. 在實際使用中,分析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.html
JVM系列三:JVM參數設置、分析, http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
Java 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.html
JDK5.0垃圾收集優化之--Don't Pause, http://calvin.iteye.com/blog/91905
Java HOTSPOT VM參數大全, http://tech.sina.com.cn/s/2009-09-23/09561077572.shtml
【原】GC的默認方式, http://iamzhongyong.iteye.com/blog/1447314
JAVA啟動參數大全之三:非Stable參數, http://blog.csdn.net/sfdev/article/details/2063928
Java虛擬機學習 - 內存調優, 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.html
JVM內存狀況查看方法和分析工具, http://hi.baidu.com/kingfly666666/item/e710a4371c60b0f1e7bb7a32
虛擬機學習系列 - 附 - 虛擬機參數, http://blog.csdn.net/su1216/article/details/7780924
?JVM系列四:生產環境參數實例及分析【生產環境實例增加中】, http://www.cnblogs.com/redcreen/archive/2011/05/05/2038331.html
垃圾收集器與內存分配策略, http://raging-sweet.iteye.com/blog/1170198
JVM垃圾收集器使用調查:CMS最受歡迎 , http://blog.csdn.net/wisgood/article/details/17067203
Xms Xmx PermSize MaxPermSize 區別, http://www.cnblogs.com/mingforyou/archive/2012/03/03/2378143.html
Java虛擬機學習 - JDK可視化監控工具, http://blog.csdn.net/java2000_wl/article/details/8049707
虛擬機學習系列 - 6 - JDK工具, http://blog.csdn.net/su1216/article/details/7780857
JVM監控工具介紹jstack, jconsole, jinfo, jmap, jdb, jstat, http://hi.baidu.com/lotusxyhf/item/9cd8fcb8d6f8c1a5ebba935b
JVM 與 jstat, http://blog.sina.com.cn/s/blog_56fcfd620100hdcp.html

轉載于:https://www.cnblogs.com/abcd19880817/p/7193876.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/392051.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392051.shtml
英文地址,請注明出處:http://en.pswp.cn/news/392051.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Maven打包排除某個資源或者目錄

最近在spark streaming本地調試的時候&#xff0c;引入了一些資源文件&#xff0c;打包的時候需要給排除掉。所以就考慮使用maven的方式 詳細參考官方文檔&#xff1a;https://maven.apache.org/plugins/maven-jar-plugin/examples/include-exclude.html 排除某個資源文件 <…

android發送網絡請求沒反應,Android無法使用HttpURLConnection發送GET請求

我正在嘗試在我的應用程序中使用HttpURLConnection.我將我的請求方法設置為’GET’,但是當我嘗試檢索輸出流時,該方法將更改為’POST’&#xff01;我不確定是什么原因,但是當我使用’POST’發送請求時,我的JSON服務器(我使用JAX-RS)會返回一個空白頁面.這是我的代碼片段&#…

地圖 c-suite_C-Suite的模型

地圖 c-suiteWe’ve all seen a great picture capture an audience of stakeholders.我們所有人都看到了吸引利益相關者聽眾的美好畫面。 Let’s just all notice that the lady in the front right is not captivated by the image on the board (Photo by Christina wocin…

框架和庫的區別_框架和庫之間的區別

框架和庫的區別Developers often use the terms “library” and “framework” interchangeably. But there is a difference.開發人員經常互換使用術語“庫”和“框架”。 但是有區別。 Both frameworks and libraries are code written by someone else that is used to he…

Java—多線程實現生產者消費者模型

采用線程實現“生產者-消費者”編程的基礎模型 源代碼 消費者代碼&#xff1a; public class Consumer implements Runnable {BlockingQueue<Integer> blockingQueue;int n;CountDownLatch countDownLatch;public Consumer(BlockingQueue<Integer> blockingQueue…

動態鏈接庫.so和靜態鏈接庫.a的區別

靜態鏈接庫&#xff1a; ?擴展名&#xff1a;.a? ?編譯行為&#xff1a;在編譯的時候&#xff0c;將函數庫直接整合到執行程序中&#xff08;所以利用靜態庫編譯生成的文檔會更大&#xff09;??獨立執行的狀態&#xff1a;編譯成功的可執行文件可以獨立運行&#xff0c;不…

華為鴻蒙系統封閉,谷歌正式“除名”華為!“親兒子”榮耀表示:暫不考慮,鴻蒙OS處境尷尬...

我們都知道&#xff0c;目前智能手機最常用操作系統就是IOS和安卓&#xff0c;占據手機系統超過99%的市場份額。由于IOS系統的封閉性&#xff0c;國內手機廠商基本上都是使用谷歌的開源安卓系統。當然華為也不例外&#xff0c;一直使用的都是安卓系統。可以說&#xff0c;安卓系…

使用vue-cli腳手架搭建簡單項目框架

1.首先已經安裝了node,最好版本6以上。 2.安裝淘寶鏡像 大家都知道國內直接使用 npm 的官方鏡像是非常慢的&#xff0c;這里推薦使用淘寶 NPM 鏡像。這樣就可以直接使用cnpm了。 npm install -g cnpm --registryhttps://registry.npm.taobao.org如果過程出差&#xff0c;是否安…

sap中泰國有預扣稅設置嗎_泰國餐廳密度細分:帶有K-means聚類的python

sap中泰國有預扣稅設置嗎Hi! I am Tung, and this is my first stories for my weekend project. What inspired this project is that I have studied to become data scientist for almost two years now mostly from Youtube, coding sites and of course, Medium ,but my l…

自動化yaml文件_從YAML到TypeScript:開發人員對云自動化的看法

自動化yaml文件The rise of managed cloud services, cloud-native, and serverless applications brings both new possibilities and challenges. More and more practices from software development processes like version control, code review, continuous integration,…

SQL SERVER-Extendevent系統視圖

--獲得擴展事件的事件select name,description from sys.dm_xe_objects where object_typeevent order by name--獲得各事件的字段 select c.name,c.description from sys.dm_xe_object_columns c inner join sys.dm_xe_objects o on o.namec.object_name where o.name…

Java—簡單的注冊頁面

根據所提供的界面&#xff0c;編寫 register.html 文件 源代碼 empty.jsp <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>error</title> </head> <body> <H1><…

【深度學習系列】用PaddlePaddle和Tensorflow實現經典CNN網絡AlexNet

上周我們用PaddlePaddle和Tensorflow實現了圖像分類&#xff0c;分別用自己手寫的一個簡單的CNN網絡simple_cnn和LeNet-5的CNN網絡識別cifar-10數據集。在上周的實驗表現中&#xff0c;經過200次迭代后的LeNet-5的準確率為60%左右&#xff0c;這個結果差強人意&#xff0c;畢竟…

圖片獲取像素坐標html,HTML5畫布Canvas圖片抽取、像素信息獲取、命中檢測

今天主要介紹canvas中比較強大的功能比如將畫布內容抽取為圖片獲取、修改畫布的像素信息以及畫布的命中檢測首先我仍然需要創建畫布圖片抽取首先要明確的一點是toDataURL()是canvas對象自身的方法而不是環境對象的這個方法會將canvas的內容抽取為一張圖片(base64編碼)我們來看一…

CentOS6 下Samba服務器的安裝與配置

原地址&#xff1a;http://www.cnblogs.com/mchina/archive/2012/12/18/2816717.html 一、簡介 Samba是一個能讓Linux系統應用Microsoft網絡通訊協議的軟件&#xff0c;而SMB是Server Message Block的縮寫&#xff0c;即為服務器消息塊 &#xff0c;SMB主要是作為Microsoft的網…

傅里葉變換 直觀_A / B測試的直觀模擬

傅里葉變換 直觀Many of us have heard, read, or even performed an A/B Test before, which means we have conducted a statistical test at some point. Most of the time, we have worked with data from first or third-party sources and performed these tests with ea…

tableau for循環_Tableau for Data Science and Data Visualization-速成課程

tableau for循環Tableau is software that can help you see and understand your data. It is used for data science and data visualization. Tableau allows you to connect to almost any database, drag and drop to create visualizations, and share with a click.Tabl…

請求接口時使用時間戳

&tnew Date().getTime()轉載于:https://www.cnblogs.com/Glant/p/11271960.html

Java—servlet簡單使用

【步驟 1】創建一個名為 input.html 的 HTML 頁面&#xff0c;其中包括一個表單&#xff0c;表單中包含兩 個文本域&#xff0c;分別供用戶輸入學號和姓名&#xff0c;該頁面也包含提交和重置按鈕。 【步驟 2】定義一個名為 com.demo.Student 類&#xff0c;其中包括學號 sno 和…

phpstrom+phpstudy+postman

1.打開phpstudy xdebug 擴展 2.修改php.ini [XDebug]xdebug.profiler_output_dir"D:\phpStudy\tmp\xdebug"xdebug.trace_output_dir"D:\phpStudy\tmp\xdebug"zend_extension"D:\phpStudy\php\php-5.5.38\ext\php_xdebug.dll";是否允許Xdebug跟蹤…