前言
JVM調優的本質:並不是爲了顯著的提升系統的性能,不是說調優過後,性能就能提升幾倍或者十幾倍,主要調的是穩定性。如果系統出現了頻繁的垃圾回收,這個系統是不穩定的,所以就需要我們來進行jvm調優,調整垃圾回收的頻次
一、GC調優原則
1、調優的原則
- 大多數的 java 應用不需要 GC 調優
- 大部分需要 GC 調優的的,不是參數問題,是代碼問題
- 在實際使用中,分析 GC 情況優化代碼比優化 GC 參數要多得多;
- GC 調優是最後的手段
2、調優的目的
GC 的時間夠小
GC 的次數夠少
發生 Full GC 的週期足夠的長,時間合理,最好是不發生
3、判斷是否需要調優的指標
Minor GC 執行時間不到 50ms;
Minor GC 執行不頻繁,約 10 秒一次;
Full GC 執行時間不到 1s;
Full GC 執行頻率不算頻繁,不低於 10 分鐘 1 次;
二、GC調優步驟
1、監控gc狀態
使用各種 JVM 工具,查看當前日誌,分析當前 JVM 參數設置,並且分析當前堆內存快照和 gc 日誌,根據實際的各區域內存劃分和 GC 執行時間,覺得是否進行優化
2、分析結果,判斷是否需要優化
如果各項參數設置合理,系統沒有超時日誌出現,GC 頻率不高,GC 耗時不高,那麼沒有必要進行 GC 優化;如果 GC 時間超過 1-3 秒,或者頻繁 GC,則 必須優化;
3、調整GC類型和內存分配
如果內存分配過大或過小,或者採用的 GC 收集器比較慢,則應該優先調整這些參數,並且先找 1 臺或幾臺機器進行 beta,然後比較優化過的機器和沒有 優化的機器的性能對比,並有針對性的做出最後選擇;
4、不斷的分析和調整
通過不斷的試驗和試錯,分析並找到最合適的參數
5、全面應用參數
如果找到了最合適的參數,則將這些參數應用到所有服務器,並進行後續跟蹤。
閱讀GC日誌
主要關注 MinorGC 和 FullGC 的回收效率(回收前大小和回收比較)、回收的時間。 -XX:+UseSerialGC
- 以參數-Xms5m -Xmx5m -XX:+PrintGCDetails -XX:+UseSerialGC 爲例:
[DefNew: 1855K->1855K(1856K), 0.0000148 secs][Tenured: 2815K->4095K(4096K), 0.0134819 secs] 4671K DefNew 指明瞭收集器類型,而且說明了收集發生在新生代。
1855K->1855K(1856K)表示,回收前 新生代佔用 1855K,回收後佔用 1855K,新生代大小 1856K。 0.0000148 secs 表明新生代回收耗時。
Tenured 表明收集發生在老年代
2815K->4095K(4096K), 0.0134819 secs:含義同新生代
最後的 4671K 指明堆的大小。 - -XX:+UseParNewGC
收集器參數變爲-XX:+UseParNewGC,日誌變爲:
[ParNew: 1856K->1856K(1856K), 0.0000107 secs][Tenured: 2890K->4095K(4096K), 0.0121148 secs] 收集器參數變爲-XX:+ UseParallelGC 或 UseParallelOldGC,日誌變爲:
[PSYoungGen: 1024K->1022K(1536K)] [ParOldGen: 3783K->3782K(4096K)] 4807K->4804K(5632K),
-XX:+UseConcMarkSweepGC
CMS 收集器和 G1 收集器會有明顯的相關字樣 -XX:+UseG1GC
三、GC實戰
1、項目啓動的優化
- 開啓日誌分析 -XX:+PrintGCDetails 發現有多次 GC 包括 FullGC
- 調整 Metadata 空間 -XX:MetaspaceSize=64m
- 減少 Minor gc 次數,增加參數 -Xms500m
- 減少 Minor gc 次數,調整參數 -Xms1000m
- 增加新生代比重,增加參數 -Xmn900m GC 減少至 1 次
- 加大新生代,調整參數 -Xms2000m -Xmn1800m 還是避免不了 GC,沒有必要調整這麼大,資源浪費
2、項目運行過程中優化
使用 jmeter 同時訪問項目接口進行壓測,使用 40 個線程,循環 2500 次進行壓力測試,觀察併發的變化。
jmeter 的聚合報告的參數解釋
2.1使用單線程 GC -XX:+UseSerialGC
吞吐量:11711.9/s
最長耗時請求:401ms
2.2使用多線程GC -XX:+UseParNewGC
吞吐量有一定上升,整體耗時少了一秒
2.3 使用 CMS -XX:+UseConcMarkSweepGC
CMS 採用了併發收集,所以 STW 的時間較小,吞吐量較單線程有一定提高,最大請求時間 MAX 有明顯的下降。
2.4使用 G1 -XX:+UseG1GC
G1 這裏的吞吐量是最大的,最大請求時間 MAX 有明顯的下降。
在請求中new 出一個1M大小的0bject,再進行壓測
吞吐量急劇下降,也驗證了我們JVM調優的原則
3、推薦策略
3.1新生代大小
- 響應時間優先的應用:儘可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇).在此種情況下,新生代收集發生的頻率也是最小 的.同時,減少到達老年代的對象.
- 吞吐量優先的應用:儘可能的設置大,可能到達 Gbit 的程度.因爲對響應時間沒有要求,垃圾收集可以並行進行,一般適合 8CPU 以上的應用.
- 避免設置過小.當新生代設置過小時會導致:1.MinorGC 次數更加頻繁 2.可能導致 MinorGC 對象直接進入老年代,如果此時老年代滿了,會觸發FullGC.
3.2老年代大小
- 響應時間優先的應用:老年代使用併發收集器,所以其大小需要小心設置,一般要考慮併發會話率和會話持續時間等一些參數.如果堆設置小了,可 以會造成內存碎 片,高回收頻率以及應用暫停而使用傳統的標記清除方式; 如果堆大了,則需要較長的收集時間.最優化的方案,一般需要參考以下數據獲得:
併發垃圾收集信息、持久代併發收集次數、傳統 GC 信息、花在新生代和老年代回收上的時間比例。 - 吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的新生代和一個較小的老年代.原因是,這樣可以儘可能回收掉大部分短期對象,減少中期的對象,而 老年代盡存放長期存活對象
jvm優化:逃逸分析
參數:
-XX:+DoEscapeAnalysis:啓用逃逸分析(默認打開) -XX:+EliminateAllocations:標量替換(默認打開)
-XX:+UseTLAB 本地線程分配緩衝(默認打開)
如果是逃逸分析出來的對象可以在棧上分配的話,那麼該對象的生命週期就跟隨線程了,就不需要垃圾回收,如果是頻繁的調用此方法則可以得到很大的性能提高(不用觸發GC)
採用了逃逸分析–對象在棧上分配:
沒有逃逸分析—對象都在堆上分配(觸發頻次 GC,加重負擔):