JVM調優實戰:G1中的to-space exhausted問題


最近剛剛將自己的一個應用從CMS升級到G1,在一天早上,剛剛到辦公室坐下,就收到手機一陣報警,去查看了監控,發現機器的內存出現了一個90度的漲幅,如下圖所示:

image.png

在查看GC日志后,發現那個時間點附近出現了“to-space exhausted”這種日志(關於G1的日志學習,參見我之前的文章:【譯】深入理解G1的GC日志(一)

image.png

在這里,我比較奇怪的是為啥to-sapce exhausted會導致整個機器的內存激增。我們JVM團隊同學給我的解釋是:老區不夠了,這個時候會把young區所有對象不管死活都轉成old區對象,所以總的內存使用量會暴增。這一個知識點,我之前學習G1的時候還真沒有get到(關於G1的基本知識,參見之前的文章:可能是最全面的G1學習筆記)。

不過,我有另外一個疑問:xmx和xms相同的話堆空間應該不變,一開始就分配5g,然后加上非堆內存,那么java進程起來后就會超過5g,這是沒問題的;但是這里利用空閑的內存也應該是利用堆上的空間,然后整體的內存塊應該已經分配出去了,應該不會出現機器內存激增的情況。JVM團隊的同學給我解釋道:沒有,第一次讀寫到了才會實際從os分配出來物理內存。

針對上面的問題,我們最終確定了下面的調優建議:

  • 這次沒有發生FGC,可能是由於我前面將xmx和xms調大了導致的,這次准備將xmx和xms先調回到原來的值;
  • 加上HeapDumpAfterFullGC參數,下次再發生類似情況的時候,就會觸發FGC,然后自動dump堆內存,就可以針對堆內存進行分析,看看是什么對象占用了這么多內存,然后就可以針對性優化。

關於to-space exhausted的更多總結

基於上面這個問題,我又去找了一些資料,整理如下。

《Java性能權威指南》

在這本書的123頁有提到,上面這種情況屬於晉升失敗的情況——G1收集器完成了標記階段,開始啟動混合式垃圾收集,准備要清理老年代分區,但是老年代分區在垃圾收集器釋放出足夠的空間之前就已經被耗盡了。這種失敗通常意味着混合式垃圾收集需要更迅速得完成垃圾收集,每次新生代垃圾收集需要處理更多的老年代分區。一般來說,一系列的to-space exhausted之后會跟着一次FGC。

在我們上面的這個例子中,是old區的使用速度超過了垃圾收集器的回收速度,因此可以考慮兩種調優的思路

  • 讓G1更早得啟動混合式垃圾收集周期,通過調小-XX:InitiatingHeapOccupancyPercent=N這個參數,默認情況下該參數是45(PS:這個參數表示的是占用整個堆內存的比例),不過,這個參數也不能調得太小,否則會導致過多的並發收集周期和混合式垃圾收集,給應用早成過多的停頓。
  • 除了考慮增加速度,還可以考慮增加每次混合式垃圾收集收集的Old分區數量,通過調整-XX:G1MixedGCCountTarget=N參數可以控制每個混合式周期中回收的Old分區數量,該參數的默認值是8;

《Java性能調優指南》

要特別關注日志片段中的"to-space exhausted"和“Evacuation Failure”兩個日志,如下圖所示。可以看出,Evacuation Failure消耗了684.1ms,也就是說,這次轉移失敗導致了將近1s的應用暫停。

image.png

這種情況屬於轉移失敗,這本書給出了兩點建議:

  • 和《Java性能權威指南》一樣,也建議調小-XX:InitiatingHeapOccupancyPercent=N這個參數的值,因為轉移失敗的代價比多執行一些並發標記周期高很多
  • 建議通過調整-XX:ConcGCThreads,增加用於垃圾收集的線程個數,代價是會多一些CPU的消耗;也就是會占用Java應用的CPU時間,這一點也需要權衡一下。
  • 有時候轉移失敗是由於survivor分區中沒有足夠的空間容納新晉升的對象,如果是這種情況,還可以考慮增加-XX:G1ReservePercent的大小,在G1中這個默認值是10%。

總結

JVM參數的調優,是一個不斷推導和嘗試的過程,其中最重要的數據就是GC日志和Java堆內存快照,因此:(1)在JVM參數中一定要設置HeapDumpAfterFullGC和HeapDumpOnOutOfMemoryError兩個參數,可以在發送FGC和OOM的時候將當時的Java堆情況記錄下來,用於事后分析;(2)GC日志要單獨打印到一個日志文件中,方便分析,如果不特別設置,GC日志會打印到stdout.log中,會有其他的日志混合在中間,影響問題排查。

JVM的參數調優並不是萬能的,發生OOM或者FGC的時候,業務代碼中也一定有不合理的地方,需要做合理的限制和優化,不能將所有的事情都交給JVM抗。


本號專注於后端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這里有所收獲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM