ZGC的7種垃圾回收觸發時機

ZGC中,爲了實現更高的性能,儘量避免進行同步垃圾回收,也就是說盡量避免觸發同步的垃圾回收的消息。ZGC中觸發同步消息的場景也比較少,總體以觸發異步消息爲主。異步消息主要由ZDirector根據規則判斷是否可以觸發,在ZDirector流程圖中【瞭解更多,可以閱讀小編的另一篇文章:ZGC線程之時鐘觸發器和消息觸發】介紹了ZDirector有4種觸發規則,本文主要介紹這4種規則是如何觸發的,最後,還會簡要介紹其他的垃圾回收消息是如何觸發的。
一、基於固定時間間隔觸發
ZDirector提供的第一個規則就是基於固定時間間隔觸發垃圾回收。這個規則的目的非常簡單,就是希望ZGC的垃圾回收器以固定的頻率觸發。在這一些場景中非常有用,例如我們的應用程序在晚上請求量比較低的情況下運行了很長時間,但是ZGC不滿足其他垃圾回收器的觸發條件,所以一直不會觸發垃圾回收,這通常沒什麼問題,如果在早上某一個時間點開始請求量暴增,這可能導致內存使用也暴增,而垃圾回收器來不及回收垃圾對象,將降低應用系統的吞吐量。所以ZGC提供了基於固定時間間隔觸發垃圾回收的規則。
這個規則的實現也非常簡單,就是判斷前一次垃圾回收結束到當前時間是否超過時間間隔的閾值,如果超過,則觸發垃圾回收,如果不滿足,則直接返回。
需要說明的是,時間間隔由一個參數ZCollectionInterval來控制,這個參數的默認值爲0,表示不需要觸發垃圾回收。實際工作中,可以根據場景設置該參數。
二、預熱規則觸發
ZDirector提供的第二個規則是預熱啓動垃圾回收。爲什麼設計這一規則?設計這一規則的目的是當JVM剛啓動時,還沒有足夠的數據來主動觸發垃圾回收的啓動,所以設置了預熱規則。
預熱規則指的是JVM啓動後,當發現堆空間使用率達到10%、20%和30%時,會主動地觸發垃圾回收。ZGC設計前3次垃圾回收可由預熱規則觸發,也就是說當垃圾回收觸發(無論是由預熱規則,還是主動觸發垃圾回收)的次數超過3次時,預熱規則將不再生效。
三、根據分配速率
ZDirector提供的第三個規則是根據分配速率來預測是否能觸發垃圾回收。這一規則設計的思路是:
1)收集數據:在程序運行時,收集過去一段時間內垃圾回收發生的次數和執行的時間、內存分配的速率MEMratio和當前空閒內存的大小MEMfree。
2)計算:根據過去垃圾回收發生的情況預測下一次垃圾回收發生的時間TIMEgc,按照內存分配的速率預測空閒內存能支撐應用程序運行的實際時間TIMEoom,例如TIMEoom=MEMfree/MEMratio。
3)設計規則:如當TIMEoom小於TIMEgc(垃圾回收的時間),則可以啓動垃圾回收。這個規則的含義是如果從現在起到OOM發生前開始執行垃圾回收,剛好在OOM發生前完成垃圾回收的動作,從而避免OOM。在ZGC中ZDirector是週期運行的,所以在計算時還應該把OOM的時間減去採樣週期的時間,採樣週期記爲TIMEinterval,則規則爲TIMEoom<TIMEgc+TIMEinterval時觸發垃圾回收。
那麼最主要的任務就變成了如何預測下一次垃圾回收時間TIMEgc和內存分配速率MEMratio(因爲MEMfree是已知數據,無需額外處理)。
我們以預測垃圾回收時間TIMEgc爲例來看看如何預測。最簡單也最直觀的思路是,根據已經發生的垃圾回收所使用的時間來預測下一次垃圾回收可能花費的時間,那麼就有了如下思路:
1)收集過去一段時間內垃圾回收發生的次數和時間,取過去N次垃圾回收的平均時間作爲下一次垃圾回收的預測時間;這一方法最爲直觀,但是準確度可能有待提高。
2)收集過去一段時間內垃圾回收發生的次數和時間,建立一個邏輯迴歸模型,從而預測下一次垃圾回收的預測時間;這一方法雖然比第一種方法有改進,根據垃圾回收的趨勢來預測下一次垃圾回收的時間,但這一方法最大的問題是邏輯迴歸模型太簡單,實際上如果我們能提供更多的輸入,比如應用程序使用內存的情況、線程數等建立動態模型,這應該是一個非常好的方法。
3)使用衰減平均時間來預測下一次垃圾回收花費的時間。衰減平均方法實際上是第一種方法和第二種方法組合後的一種簡化實現。它是一種簡單的數學方法,用來計算一組數據的平均值,但是在計算平均值的時候最新的數據有更高的權重,即強調近期數據對結果的影響。在G1中預測下一次垃圾回收時間採用的就是這種方法。
4)直接採用已經成熟的模型來預測下一次垃圾回收時間。ZGC中主要是基於正態分佈來預測。
學過概率論的同學大多知道這一概念。我們先來回顧一下正態分佈。首先它是一條中間高,兩端逐漸下降且完全對稱的鐘形曲線。正態分佈也非常容易理解,指的是大多數數據應該集中在中間附近,少數異常的情況纔會落在兩端。
對於垃圾回收算法中的數據:內存的消耗時間,垃圾回收的時間也應該符合這樣的分佈。注意,並不是說G1中的停頓預測模型不正確或者效果不好,而是說使用正態分佈來做預測有更強的數學理論支撐。
四、主動觸發
ZDirector提供的第四個規則是主動觸發規則,該規則是爲了應用程序在吞吐量下降的情況下,當滿足一定條件時,還可以執行垃圾回收。這裏滿足一定條件指的是:
1)從上一次垃圾回收完成到當前時間,應用程序新增使用的內存達到堆空間的10%。
2)從上一次垃圾回收完成到當前時間已經過去了5min,記爲TIMEelapsed。
如果這兩個條件同時滿足,預測垃圾回收時間爲TIMEgc,定義規則:如果NUMgc * TIMEgc < TIMEelapsed,則觸發垃圾回收。其中NUMgc是ZGC設計的常量,假設應用程序的吞吐率從50%下降到1%,需要觸發一次垃圾回收。
這個規則實際上是爲了彌補程序吞吐率驟降且長時間不執行垃圾回收而引入的。有一個診斷參數ZProactive來控制是否開啓和關閉主動規則,默認值是true,即默認打開主動觸發規則。
實際上這個規則和第一個規則(基於固定時間間隔規則)在某些場景中有一定的重複,第一個規則只強調時間間隔,本規則除了考慮時間之外還會考慮內存的增長和吞吐率下降的快慢程度。
五、阻塞內存分配請求觸發
阻塞內存分配由參數ZStallOnOutOfMemory控制,當參數ZStallOnOutOfMemory爲true時進行阻塞分配,如果不能成功分配內存,則觸發阻塞內存分配。
注意:該觸發請求是異步的,並非同步消息。頁面阻塞分配會觸發垃圾回收,直到垃圾回收完成併成功分配頁面爲止。因爲是異步消息,所以頁面阻塞分配請求需要額外的實現等待成功分配的功能,其實非常簡單,可以通過一個循環來實現。
那爲什麼ZGC不把阻塞內存分配實現成同步消息,而是通過異步消息加上循環的方式?
原因在於同步消息請求的線程在發出同步消息後是通過通知等待機制完成的,通知等待機制通常會讓出CPU,而頁面阻塞分配採用異步消息加上循環的方式,這樣的設計可以減少頁面分配時因線程調度帶來的額外開銷。從這一點也可以看出,設計一款優秀的軟件,需要從每一個細節出發,並仔細斟酌。
六、外部觸發
外部觸發是指在Java代碼中顯式地調用System.gc()函數,在JVM執行該函數時,會觸發垃圾回收。該觸發請求是從用戶代碼主動觸發的,從編程角度來看,說明程序員認爲此時需要進行垃圾回收(當然首先是程序員正確使用System.gc()函數),所以ZGC把該觸發規則設計爲同步請求,只有在執行完垃圾回收後,才能進行後續代碼的執行。
七、元數據分配觸發
元數據分配失敗時,ZGC會嘗試進行垃圾回收以確保元數據能正確分配。
異步垃圾回收後會嘗試是否可以分配元數據對象空間,如果不能,將嘗試進行同步垃圾回收後可以分配元數據對象空間,如果還不成功,則嘗試擴展元數據空間,再分配成功則返回內存空間,不成功則返回NULL。
更多內容,請關注微信公衆號:架構視角

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章