VM記憶體的系統級的調優主要的目的是減少GC的頻率和Full GC的次數。
1.Full GC
會對整個堆進行整理,包括Young、Tenured和Perm。Full GC因為需要對整個堆進行回收,所以比較慢,因此應該儘可能減少Full GC的次數。
2.導致Full GC的原因
1)年老代(Tenured)被寫滿
調優時儘量讓物件在新生代GC時被回收、讓物件在新生代多存活一段時間和不要建立過大的物件及陣列避免直接在舊生代建立物件 。
2)持久代Pemanet Generation空間不足
增大Perm Gen空間,避免太多靜態物件 , 控制好新生代和舊生代的比例
3)System.gc()被顯示呼叫
垃圾回收不要手動觸發,儘量依靠JVM自身的機制
在對JVM調優的過程中,很大一部分工作就是對於FullGC的調節,下面詳細介紹對應JVM調優的方法和步驟。
二、JVM效能調優方法和步驟
1.監控GC的狀態
使用各種JVM工具,檢視當前日誌,分析當前JVM引數設定,並且分析當前堆記憶體快照和gc日誌,根據實際的各區域記憶體劃分和GC執行時間,覺得是否進行最佳化。
舉一個例子: 系統崩潰前的一些現象:
之後系統會無法響應新的請求,逐漸到達OutOfMemoryError的臨界值,這個時候就需要分析JVM記憶體快照dump。
2.生成堆的dump檔案
透過JMX的MBean生成當前的Heap資訊,大小為一個3G(整個堆的大小)的hprof檔案,如果沒有啟動JMX可以透過Java的jmap命令來生成該檔案。
3.分析dump檔案
開啟這個3G的堆資訊檔案,顯然一般的Window系統沒有這麼大的記憶體,必須藉助高配置的Linux,幾種工具開啟該檔案:
備註:檔案太大,建議使用Eclipse專門的靜態記憶體分析工具Mat開啟分析。
4.分析結果,判斷是否需要最佳化
如果各項引數設定合理,系統沒有超時日誌出現,GC頻率不高,GC耗時不高,那麼沒有必要進行GC最佳化,如果GC時間超過1-3秒,或者頻繁GC,則必須最佳化。
注:如果滿足下面的指標,則一般不需要進行GC:
5.調整GC型別和記憶體分配
如果記憶體分配過大或過小,或者採用的GC收集器比較慢,則應該優先調整這些引數,並且先找1臺或幾臺機器進行beta,然後比較最佳化過的機器和沒有最佳化的機器的效能對比,並有針對性的做出最後選擇。
6.不斷的分析和調整
透過不斷的試驗和試錯,分析並找到最合適的引數,如果找到了最合適的引數,則將這些引數應用到所有伺服器。
cms引數最佳化步流程
下面我再繼續介紹下JVM的關鍵引數配置(僅用於參考)。
JVM調優引數參考
1.針對JVM堆的設定,一般可以透過-Xms -Xmx限定其最小、最大值,為了防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間,通常把最大、最小設定為相同的值;
2.年輕代和年老代將根據預設的比例(1:2)分配堆記憶體, 可以透過調整二者之間的比率NewRadio來調整二者之間的大小,也可以針對回收代。
比如年輕代,透過 -XX:newSize -XX:MaxNewSize來設定其絕對大小。同樣,為了防止年輕代的堆收縮,我們通常會把-XX:newSize -XX:MaxNewSize設定為同樣大小。
3.年輕代和年老代設定多大才算合理
1)更大的年輕代必然導致更小的年老代,大的年輕代會延長普通GC的週期,但會增加每次GC的時間;小的年老代會導致更頻繁的Full GC
2)更小的年輕代必然導致更大年老代,小的年輕代會導致普通GC很頻繁,但每次的GC時間會更短;大的年老代會減少Full GC的頻率
如何選擇應該依賴應用程式物件生命週期的分佈情況: 如果應用存在大量的臨時物件,應該選擇更大的年輕代;如果存在相對較多的持久物件,年老代應該適當增大。但很多應用都沒有這樣明顯的特性。
在抉擇時應該根 據以下兩點:
(1)本著Full GC儘量少的原則,讓年老代儘量快取常用物件,JVM的預設比例1:2也是這個道理 。
(2)透過觀察應用一段時間,看其他在峰值時年老代會佔多少記憶體,在不影響Full GC的前提下,根據實際情況加大年輕代,比如可以把比例控制在1:1。但應該給年老代至少預留1/3的增長空間。
4.在配置較好的機器上(比如多核、大記憶體),可以為年老代選擇並行收集演算法: -XX:+UseParallelOldGC 。
5.執行緒堆疊的設定:每個執行緒預設會開啟1M的堆疊,用於存放棧幀、呼叫引數、區域性變數等,對大多數應用而言這個預設值太了,一般256K就足用。
理論上,在記憶體不變的情況下,減少每個執行緒的堆疊,可以產生更多的執行緒,但這實際上還受限於作業系統。
VM記憶體的系統級的調優主要的目的是減少GC的頻率和Full GC的次數。
1.Full GC
會對整個堆進行整理,包括Young、Tenured和Perm。Full GC因為需要對整個堆進行回收,所以比較慢,因此應該儘可能減少Full GC的次數。
2.導致Full GC的原因
1)年老代(Tenured)被寫滿
調優時儘量讓物件在新生代GC時被回收、讓物件在新生代多存活一段時間和不要建立過大的物件及陣列避免直接在舊生代建立物件 。
2)持久代Pemanet Generation空間不足
增大Perm Gen空間,避免太多靜態物件 , 控制好新生代和舊生代的比例
3)System.gc()被顯示呼叫
垃圾回收不要手動觸發,儘量依靠JVM自身的機制
在對JVM調優的過程中,很大一部分工作就是對於FullGC的調節,下面詳細介紹對應JVM調優的方法和步驟。
二、JVM效能調優方法和步驟
1.監控GC的狀態
使用各種JVM工具,檢視當前日誌,分析當前JVM引數設定,並且分析當前堆記憶體快照和gc日誌,根據實際的各區域記憶體劃分和GC執行時間,覺得是否進行最佳化。
舉一個例子: 系統崩潰前的一些現象:
每次垃圾回收的時間越來越長,由之前的10ms延長到50ms左右,FullGC的時間也有之前的0.5s延長到4、5sFullGC的次數越來越多,最頻繁時隔不到1分鐘就進行一次FullGC年老代的記憶體越來越大並且每次FullGC後年老代沒有記憶體被釋放之後系統會無法響應新的請求,逐漸到達OutOfMemoryError的臨界值,這個時候就需要分析JVM記憶體快照dump。
2.生成堆的dump檔案
透過JMX的MBean生成當前的Heap資訊,大小為一個3G(整個堆的大小)的hprof檔案,如果沒有啟動JMX可以透過Java的jmap命令來生成該檔案。
3.分析dump檔案
開啟這個3G的堆資訊檔案,顯然一般的Window系統沒有這麼大的記憶體,必須藉助高配置的Linux,幾種工具開啟該檔案:
Visual VMIBM HeapAnalyzerJDK 自帶的Hprof工具Mat(Eclipse專門的靜態記憶體分析工具)推薦使用備註:檔案太大,建議使用Eclipse專門的靜態記憶體分析工具Mat開啟分析。
4.分析結果,判斷是否需要最佳化
如果各項引數設定合理,系統沒有超時日誌出現,GC頻率不高,GC耗時不高,那麼沒有必要進行GC最佳化,如果GC時間超過1-3秒,或者頻繁GC,則必須最佳化。
注:如果滿足下面的指標,則一般不需要進行GC:
Minor GC執行時間不到50ms;Minor GC執行不頻繁,約10秒一次;Full GC執行時間不到1s;Full GC執行頻率不算頻繁,不低於10分鐘1次;5.調整GC型別和記憶體分配
如果記憶體分配過大或過小,或者採用的GC收集器比較慢,則應該優先調整這些引數,並且先找1臺或幾臺機器進行beta,然後比較最佳化過的機器和沒有最佳化的機器的效能對比,並有針對性的做出最後選擇。
6.不斷的分析和調整
透過不斷的試驗和試錯,分析並找到最合適的引數,如果找到了最合適的引數,則將這些引數應用到所有伺服器。
cms引數最佳化步流程
下面我再繼續介紹下JVM的關鍵引數配置(僅用於參考)。
JVM調優引數參考
1.針對JVM堆的設定,一般可以透過-Xms -Xmx限定其最小、最大值,為了防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間,通常把最大、最小設定為相同的值;
2.年輕代和年老代將根據預設的比例(1:2)分配堆記憶體, 可以透過調整二者之間的比率NewRadio來調整二者之間的大小,也可以針對回收代。
比如年輕代,透過 -XX:newSize -XX:MaxNewSize來設定其絕對大小。同樣,為了防止年輕代的堆收縮,我們通常會把-XX:newSize -XX:MaxNewSize設定為同樣大小。
3.年輕代和年老代設定多大才算合理
1)更大的年輕代必然導致更小的年老代,大的年輕代會延長普通GC的週期,但會增加每次GC的時間;小的年老代會導致更頻繁的Full GC
2)更小的年輕代必然導致更大年老代,小的年輕代會導致普通GC很頻繁,但每次的GC時間會更短;大的年老代會減少Full GC的頻率
如何選擇應該依賴應用程式物件生命週期的分佈情況: 如果應用存在大量的臨時物件,應該選擇更大的年輕代;如果存在相對較多的持久物件,年老代應該適當增大。但很多應用都沒有這樣明顯的特性。
在抉擇時應該根 據以下兩點:
(1)本著Full GC儘量少的原則,讓年老代儘量快取常用物件,JVM的預設比例1:2也是這個道理 。
(2)透過觀察應用一段時間,看其他在峰值時年老代會佔多少記憶體,在不影響Full GC的前提下,根據實際情況加大年輕代,比如可以把比例控制在1:1。但應該給年老代至少預留1/3的增長空間。
4.在配置較好的機器上(比如多核、大記憶體),可以為年老代選擇並行收集演算法: -XX:+UseParallelOldGC 。
5.執行緒堆疊的設定:每個執行緒預設會開啟1M的堆疊,用於存放棧幀、呼叫引數、區域性變數等,對大多數應用而言這個預設值太了,一般256K就足用。
理論上,在記憶體不變的情況下,減少每個執行緒的堆疊,可以產生更多的執行緒,但這實際上還受限於作業系統。