JAVA GC優化入門

爲何須要優化GC?

  JAVA的GC是面試必考的題目,但是在實際項目中何時使用GC哪?或者應該何時優化GC哪?有句名言:「GC優化永遠是最後一項任務」。web

  在使用GC以前,應該考慮一下進行GC的最根本緣由:垃圾收集器須要清除在程序中建立的對象,GC執行的次數即須要被垃圾收集器清理的對象個數,與建立對象的數量成正比,所以,首先應該減小建立對象的數量,咱們應該從小事作起,好比須要使用StringBuilder 或者StringBuffer 來替代String;應該儘可能少的輸出日誌;面試

  可是,咱們知道有些狀況會讓咱們一籌莫展,咱們眼睜睜的看着XML以及JSON解析佔用了大量的內存。即使咱們已經儘量少的使用String以及儘可能少的輸出日誌,可是大量的臨時內存仍然被用於XML或者JSON解析,例如10-100MB。可是,捨棄XML和JSON是很難的;或者程序偶爾內存溢出;或者假死影響到了正常的服務,這個時候就能夠考慮優化GC了。服務器

  那麼優化GC能夠首相到兩個方面:一、一個是將轉移到老年代的對象數量降到最少;二、另外一個是減小Full GC的執行時間。性能

將轉移到年老代的對象數量降到最少

  分代垃圾回收策略是由Oracle JVM提供,不包括能夠在JDK7以及更高版本中使用的G1 GC。換句話說,對象被建立在伊甸園空間,然後轉化到倖存者空間,最終剩餘的對象被送到老年代。某些比較大的對象會在被建立在伊甸園空間後,直接轉移到年老代空間。年老代空間上的GC處理會年輕代花費更多的時間。所以,減小被移到年老代對象的數據能夠顯著地減小Full GC的頻率。減小被移到年老代空間的對象的數量,可能會被誤解爲將對象留在新生代。可是,這是不可能的。取而代之,能夠調整新生代空間的大小測試

減小Full GC執行時間

  Full GC的執行時間比Minor GC要長不少。所以,若是Full GC花費了太多的時間(超過1秒),一些鏈接的部分可能會發生超時錯誤。這個時候若是你單純試圖經過減小年老代空間來減小Full GC的執行時間,可能會致使OutOfMemoryError 或者 Full GC執行的次數會增長。與之相反,若是你試圖經過增長老年代空間來減小Full GC執行次數,執行時間又會增長。所以,你須要將老年代空間設定爲一個「合適」的值。優化

影響GC性能的參數

 不要幻想「某我的設定了GC參數後性能獲得極大的提升,咱們爲何不和他用同樣的參數?」,由於不一樣的Web服務所建立對象的大小和他們的生命週期都不盡相同。ui

  Java GC參數設定一些參數不但沒有提升GC執行速度,反而可能致使他更慢。GC優化的最基本原則是將不一樣的GC參數用於2臺或者多臺服務器,並進行對比,並將那些被證實提升了性能或者減小了GC執行時間的參數應用於服務器。下面這個表格列出了GC參數中與內存大小相關的,能夠影響性能的參數:spa

                 表1:GC優化須要考慮的Java參數日誌

定義 參數 說明
堆內存 -Xms 啓動JVM時的堆內存空間大小
  -Xmx 堆內存的最大值
年輕代 -XX:NewRatio 年輕代與年老代的比值
  -XX:NewSize 年輕代大小
  -XX:SurvivorRatio 伊甸園空間和倖存者空間的比值

  我在進行GC優化時常用-Xms,-Xmx和-XX:NewRatio。另外一個可能影響GC性能的參數是GC類型。下表列出了全部可選的GC類型(基於JDK6.0)。orm

 

分類 參數 備註
Serial GC -XX:+UseSerialGC  
Parallel GC -XX:+UseParallelGC  
-XX:ParallelGCThreads=value
Parallel Compacting GC -XX:+UseParallelOldGC  
CMS GC -XX:+UseConcMarkSweepGC  
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=value
-XX:+UseCMSInitiatingOccupancyOnly
G1 -XX:+UnlockExperimentalVMOptions 在JDK6中這兩個參數必須同時使用
-XX:+UseG1GC

GC優化過程

GC優化的過程與大多數性能改善的過程及其相似。下面是使用的GC優化過程:

  1.監控GC狀態

    首先你須要監控GC來檢查在系統執行過程當中GC的各類狀態。

  2.在分析監控結果後,決定是否進行GC優化

    在檢查GC狀態的過程當中,你應該分析監控結果以便決定是否進行GC優化,若是分析結果代表執行GC的時間只有0.1-0.3秒,那你就不必浪費時間去進行GC優化。可是,若是GC的執行時間是1-3秒,或者超過10秒,GC將勢在必行。

  3. 調整GC類型/內存空間

    若是你已經決定要進行GC優化,那麼就要選擇GC類型和設定內存空間。在這時,若是你有幾臺不一樣服務器,請時刻牢記,檢查每一臺服務器的GC參數,並進行有針對性的優化。

  4.分析結果

    在調整了GC參數並持續收集24小時以後,開始對結果進行分析,若是你幸運的話,你就找到那些最適合系統的GC參數。反之,你須要經過分析日誌來檢查內存是如何被分配的。而後你須要經過不斷的調整GC類型和內存空間大小一邊找到最佳的參數。

  5. 若是結果使人滿意,你能夠將該參數應用於全部的服務器,並中止GC優化

    有過GC優化結果使人滿意,你能夠應用於全部的服務器,下面,咱們將看到每一個步驟的具體任務。

監控GC狀態及分析結果

  查看運行中的Web Application Server (WAS)的GC狀態的最佳方法是經過jstat命令,下面這個例子展示了某個JVM在進行GC優化以前的狀態。

  如上表,咱們先看一下YGC 和YGCT,計算YGCT/YGC,172.623/3428=0.050秒(50毫秒)。這意味着新生代空間上的GC操做平均花費50毫秒。在這種狀況,你大可沒必要擔憂新生代空間上執行的GC操做。接下來,咱們來看一下FGCT 和FGC。計算FGCT/ FGC獲得19.68秒,這意味着GC的平均執行時間爲19.68秒,多是每次花費19.68秒執行了三次,也多是其中的兩次執行了1秒而另外一次執行了58秒。不論哪一種狀況,都須要進行GC優化。

  經過jstat 命令能夠很輕易地查看GC狀態,可是,分析GC的最佳方式是經過–verbosegc參數來生成日誌。若是GC執行時間知足下面全部的條件,就意味着無需進行GC優化了:

  • Minor GC執行的很快(小於50ms)
  • Minor GC執行的並不頻繁(大概10秒一次)
  • Full GC執行的很快(小於1s)
  • Full GC執行的並不頻繁(10分鐘一次)

  上面提到的數字並非絕對的;他們根據服務狀態的不一樣而有所區別,某些服務可能知足於Full GC每次0.9秒的速度,但另外一些可能不是。所以,針對不一樣的服務設定不一樣的值以決定是否進行GC優化。

  在查看GC狀態的時候有件事你須要特別注意,那就是不要只關注Minor GC 和Full GC的執行時間。還要關注GC執行的次數,例如,當新生代空間較小時,Minor GC會過於頻繁的執行(有時每秒超過1次)。另外,轉移到老年代的對象數增多,則會致使Full GC執行次數增多。所以,別忘了加上–gccapacity參數來查看具體佔用了多少空間。

設定GC類型/內存空間大小

  1.設定GC類型

  OracleJVM有5種GC類型,可是在JDK7以前的版本中,只能在Parallel GC, Parallel Compacting GC 和CMS GC之中選擇一個,對於選擇哪一個沒有明確的原則和規則,可是有一點是很明確的:CMS GC比Parallel GCs更快。

  可是,CMS GC也不老是更快。總體來看,CMS GC模式下的Full GC執行更快,不過,一旦出現並行模式失敗,他將比Parallel GC更慢。

  CONCURRENT MODE失敗

  在說明這個問題以前,首先要明白Parallel GC 和 CMS GC 最大的不一樣來自於壓縮任務。壓縮任務是經過刪除已分配內存空間中的空白空間以便壓縮內存,清理內存碎片。在Parallel GC模式下,壓縮工做在Full GC執行時進行,這會費不少時間,可是,在執行完Full GC以後,因爲可以順序地分配空間,隨後的內存可以被更快的分配。

  與之相反的,CMS GC並不進行壓縮處理,所以,CMS GC執行的更快。可是,因爲沒有壓縮,在進行磁盤清理以前,內存中會有不少空白空間。這就是說,可能沒有足夠的空間存儲大的對象,例如,雖然老年代空間還有300MB空間,可是一些10MB的對象沒法被順序的存儲。在這種狀況下,會出現「並行模式失敗」警告,並執行壓縮處理。在CMS GC模式下,壓縮處理的執行時間要比Parallel GCs長不少。這樣CMS還不準Parallel GCs效率高。綜上所述,你須要找到最適合你的系統的GC類型。每一個系統都有最適合他的GC類型等着你去尋找,若是你有6臺服務器。我建議你每兩臺設置相同的參數。並添加 –verbosegc參數,分析結果。

  2.設定內存空間大小

  下面特別精闢的展現了內存空間大小、GC執行次數和GC執行時間三者間的關係:

  • 大內存空間
    • 減少GC執行次數
    • 增長GC執行時間
  • 小內存空間
    • 減少GC執行時間
    • 增長GC執行次數

  關於如何設置內存空間的大小,沒有惟一的標準答案。若是服務器資源足夠,並且Full GC也可能在1秒內完成,設置爲10GB固然可行。但絕大多數服務器並非這樣,當內存設爲10GB時,可能要花費10~30秒來執行Full GC。固然,執行時間會隨對象的大小而改變。

  鑑於如此,咱們應該如何設定內存空間大小呢?通常來講,我建議爲500MB。不過請注意這不是讓你將WAS的內存參數設置爲–Xms500m 和–Xmx500m。根據優化GC以前的狀態,若是Full GC執行以後內存空間剩餘300MB,那麼最好將內存設置爲1GB(300MB(默認程序佔用)+ 500MB(老年代最小空間)+200MB(空閒內存))。也就是說你要爲老年代額外設置500MB。所以,若是你有三個執行服務器,內存分別設置爲1GB,1.5GB,2GB,而且檢查結果。 

  理論上來說,GC執行速度應該遵循1GB> 1.5GB> 2GB,所以1GB執行GC速度最快。可是並不說明1GB空間的Full GC會花費1秒而2GB空間會花費2秒。時間取決於服務器的性能和對象的大小。所以,最佳的方式是創建儘量多的衡量指標來監控他們。

  對於內存空間大小,你應該額外設定NewRatio參數。NewRatio參數是新生代和老年代空間的比例,即XX:NewRatio=1意味着新生代與老年代之比爲1:1。對於1GB來講就是新生代和老年代各500MB。若是NewRatio爲2,意味着新生代老年代之比爲1:2,所以該值越大,老年代空間越大,新生代空間越小。

  這看似一件不是很重要的事情,但NewRatio參數會顯著地影響整個GC的性能。若是新生代空間很小,會用更多的對象被轉移到老年代空間,這樣致使頻繁的Full GC,增長暫停時間。你能夠簡單的認爲NewRatio 爲1是最佳的選擇,可是,有時可能設置爲2或3更好,我就見過不少這樣的例子。

   如何最快的完成GC優化?對比性能測試的結果應該是最快地方法,爲每一臺服務器設置不一樣的參數並監控他們的狀態,強烈建議至少監控1或2天的數據。可是,當你對GC優化是,你要確保每次執行相同的負載。而且請求的比率,例如URL都應該是一致的。不過,即使對於專業測試人員要想精確的控制負載也是很難的,並要花費大量的時間準備。所以,相對來講比較方便和容易的方法是調整才參數,以後花費較長的時間收集結果。

相關文章
相關標籤/搜索