release note

1.3版:調整章節順序,略微修改概述和標題

1.2版:增加目錄信息

1.1版 :增加問題12,補充gc問題掛進程和優化方法論總結的章節,解釋最終優化內容。

本文基於JAVA 版本1.8.0_77,其他java版本參數默認值,執行邏輯細節上可能有所差別。

目錄

  • 本文亮點
  • gc調優效果
  • 學習的起點-知道自己不知道what
  • g1 gc執行細節
  • 參數調優方式論
    • 增加日誌列印
    • 分析統計信息
    • 分析gc日誌
    • gc問題掛進程
      • 1.寫入過猛引起的進程掛
      • 2.參數問題導致mixed gc連續出現長時間暫停
    • 優化方法論小結
  • 最終優化內容

本文亮點

講g1原理和參數語義的文章很多,隨便谷歌百度一下無數。但大多數都是對官方介紹的翻譯轉述。很少有文章介紹參數間彼此的影響,調整參數牽一髮動全身的影響有哪些。參數調優的方法論,更是沒人提及。

本文目標受眾是對g1原理和參數有所了解,想進一步了解參數彼此間聯繫,想在參數調優方面更進一步的同學。

通過一個線上gc優化的實際案例,帶你熟悉gc執行細節,了解我總結的參數調優方法論。

如果對g1不了解,可以先搜索了解一下g1原理和參數後再來看本文,會事半功倍。

gc調優效果

線上某分組的regionserver之前存在不穩定的問題。一兩個月內總會隨機出現1,2台機器,突然cpu飈高,寫延時變高引起堆積,找不到問題,只能將問題regionserver移走換一台伺服器代替。後來發現regionserver直接重啟也能解決,那是不是regionserver配置有問題?

經調研和論證,確定是gc參數配置問題,參數優化後徹底解決了該問題。

由於伺服器上還部署了datanode,存在一定的資源搶佔,regionserver gc時間,間隔有一些波動,但大體是可衡量的。

之前50g堆,young區最大15g,6秒一次young gc,16線程暫停時間100ms左右。 現在100g堆,控制 20g young區,間隔8秒一次young gc,32線程暫停時間大概90ms。

優化前,大概7分鐘一次mixed gc周期,優化後大概半小時。

優化前,mixed gc周期內頭幾次mixed gc之後,mixed gc快速惡化到400-1500毫秒之間,收尾的幾次gc基本要秒級以上,gc時間佔比短期內達到80%。優化後,mixed gc絕大多數在400ms以內,百分之幾的概率在500-900毫秒範圍,gc時間基本不會觸發超過10%的告警日誌列印。

學習的起點-知道自己不知道what

看過文檔,知道參數語義了,就可以進行參數調優了嗎?

學習有這麼幾個階段,什麼都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在不知道自己不知道的階段,誤以為自己什麼都知道,貿然調優,事倍功半,優化結果南轅北轍。

請看下面的問題,是否有明確的答案?

1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young區起始比例和最大比例的默認值。那麼young區有最小比例嗎,最小比例是多少?

2.young區的動態大小除了受-XX:MaxGCPauseMillis=100 單次gc最大暫停時間影響,受-XX:G1MaxNewSizePercent=60上限限制,還被其他因素影響嗎?

3.-XX:InitiatingHeapOccupancyPercent=45是啟動並發mixed gc的已用內存比例閾值,該比例的分母是當前堆內存。那麼分子是old區已用,是young+old區已用,還是堆內存已用?

4.一次mixed gc周期內,mixed gc之間的間隔是怎樣的,立刻執行下次,有固定時間間隔,還是受其他條件影響?

5.-XX:G1OldCSetRegionThresholdPercent=10 是單次mixed gc掃old區region數的比例。該比例的分母是old區已用region,young+old區已用region,還是堆總region?該比例設置10就會掃10%的region嗎,是否受其他條件影響?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目標次數,該數值實際邏輯是什麼,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默認值有可能90)要回收region的存活對象內存佔比,存活越低回收效果越好。如果存活比例超過85%了,這次mixed gc,這次mixed迭代周期內就一定不會回收了嗎?

8.很多技術文章都翻譯了官方文檔的這句話,mixed gc從可回收比例高的region開始回收。實際執行上是怎麼做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次數, -XX:G1OldCSetRegionThresholdPercent=10 是最大掃old區region數的比例,-XX:MaxGCPauseMillis=100 是期待的單次gc最大暫停時間。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默認值有可能90)是回收region的存活對象內存佔比。這幾個參數如何決定單次mixed gc清理多少region,一次mixed周期執行多少次mixed gc?

10.其他條件一樣,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的單次gc時間短嗎?

11.閱讀理解,下面三行mixed gc執行日誌,說明存在哪些問題?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]

6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

12.坊間傳聞,g1內存越大回收效率越差,越不穩定。即使機器資源夠分配100g的堆,也只分配50g。這麼做有道理嗎?

上面各個參數都標註了網上常見的參數解釋,了解參數解釋你能回答以上的問題嗎?如果有疑惑,那麼請看下一節。

g1 gc執行細節

-XX:G1NewSizePercent同時是young區最小比例。

young區上限還受old區大小影響,最大不超過90%(默認保留10%)-當前old區大小。當old區朝90%接近時,young區會持續減少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old區已用。

mixed gc之間是以觸發下限young區gc為間隔的,即下限eden區寫滿時,mixed gc同時清理young區和old區。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆總region數,100g堆32MB大小region,則總數3200個region,10%就是320個。該比例決定單次mixed gc掃描region數的上限。如果預期掃region時間高於預期剩餘時間,則只會掃少量region計算活躍對象比例。

-XX:G1MixedGCCountTarget=8語義是,用觸發mixed gc周期時old區的已用region數,除以8,作為每次mixed gc邏輯上最少回收的region數。

一次mixed gc掃描的region中,活躍對象比例高於-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代價高。

如果不昂貴的region數不小於最小回收數,那麼回收所有不昂貴region,昂貴region按活躍比例在mixed gc周期內整體排序。如果掃描完所有old區region,垃圾比例依然高於-XX:G1HeapWastePercent比例,那麼按活躍對象比逆序清理昂貴region,直到比例降低到閾值。

如果總region數大於最小region數但不昂貴region數不夠min,則回收min個region,昂貴region中活躍對象比最低的region填補min的缺。

如果min大於實際掃的region,會回收本次mixed gc所有掃瞄過的region數,即使region的活躍對象比超過閾值。

如果-XX:MaxGCPauseMillis過低,預期掃region時間遠大於預期剩餘時間,那麼實際掃的region會小於min數,即使掃的都是昂貴region,依然會全部回收,造成數秒級gc暫停,實際暫停時間反而比-XX:MaxGCPauseMillis大一些要長。

若干次mixed gc後,如果可回收占堆內存比例小於等於-XX:G1HeapWastePercent,本輪mixed gc周期結束。

綜上所述,參數有密切的關聯關係,調優需要全局權衡。

最後一個問題的日誌,由於-XX:MaxGCPauseMillis過低只掃描了少量region,-XX:G1MixedGCCountTarget過低min region數高,昂貴region依然要被回收,暫停時間遠大於預期到達秒級,下次掃的region更少,回收昂貴region難以釋放內存,持續惡化。堆50g,young區下限2.5g ,間隔不到1秒一次mixed gc,gc時間佔比很快超過80%。再加上偶發的memstore內存接近峰值,加上L1讀cache,加上靜態對象佔用的,總不可釋放內存比例很高,而-XX:InitiatingHeapOccupancyPercent比例過低,觸發mixed gc周期時幾乎拷貝了一遍old區所有region,內存也沒釋放多少空間,regionserver表現出持續的吞吐能力降低,服務不可用現象。

目前只遺留了一個問題,g1是否可以用大堆。容我賣個關子,讀者可以結合上面的執行細節,先自己認真思考一下,再往下看。

參數調優方式論

授人以魚不如授人以漁,我們先來探討調優的方法論。

調優方法論是,先整體分析gc運行狀況,找到瓶頸點或懷疑有問題的地方。仔細翻閱問題發生時間的gc日誌,找到有問題的信息,調優。繼續觀察。

增加日誌列印

調優首先要清楚gc運行狀況,上一篇gc探索分享里介紹了如何加列印參數,以及如何通過gceasy可視化統計信息。如果沒閱讀請先看上一篇相關內容。需要額外注意的是,gceasy對g1支持的有點小bug,gc暫停時間圖把mixed gc統計到young gc次數里了。如有圖裡有暫停時間比-XX:MaxGCPauseMillis高一個數量級的暫停時間,都是mixed gc的。

分析統計信息

通過gceasy,我們可以看到gc平均暫停時間,最大暫停時間,應用吞吐佔比。gc暫停時間分布,gc前後堆內存變化,gc暫停時間按時間周期的分布圖,單次gc垃圾回收內存量的分布圖,young區容量分布圖,old區容量分布圖,young區 promoted分布圖,gc內部各階段時間統計對比,對象創建和升代速度。

通過這些統計信息我們可以了解系統的運行情況,如young 區在多大範圍波動,平均young區 gc間隔大概是多久,升代量是多少,mixed gc周期的間隔,每次mixed gc周期回收的old 區內存,等等。掌握這些統計信息,我們就對系統運行情況有了基本了解,可以對系統是否健康做一個初步判斷。

不健康主要有兩大類,單次暫停時間過長,gc時間佔比高。這兩個問題又可以引申出單次gc暫停時間過長的次數太多,gc時間佔比過高的頻率高,mixed gc頻率高,每次mixed回收old區內存量太低,等等。

我的經驗是,50g或100g的堆內存,如果gc時間佔比超過5%,或者gc最大暫停時間超過5秒出現很多次,都是有問題的,需要優化。

如果gc日誌跨越好幾天,周期性gc時間佔比高,但平均gc時間佔比可能並不高。如果某一時間段吞吐,延時有問題,可以將這個時間段前後半小時的gc日誌截出來,單獨上傳gceasy。

只看統計信息還不夠。本人遇到過日誌周期跨越好幾天,平均gc時間佔比不到2%,最大暫停時間1秒110毫秒,但圖表上來看1秒上下的暫停總是連續出現,伴隨著周期性業務響應變慢。仔細查看gc日誌,gc時間佔比在短時間內達到了70%以上,周期性出現。所以需要進一步分析gc日誌。

分析gc日誌

gc日誌內容很多,包括每次gc前堆情況,gc後堆情況,young和mixed gc執行情況。那麼我們如何分析呢?

還是要對症下藥,找問題出現時前後的日誌。

如果暫停時間周期性或偶發出現比預期高一個數量級,可以根據統計信息看到的長時間暫停時間。搜日誌,如搜索 real=5找暫停時間5秒到6秒的 ,找到gc上下文,分析為什麼慢。

常見以下幾種問題

1. max小於等於min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]

2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

沒啥說的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的經驗是min最好控制在max的1/4以內,留冗餘給昂貴region。

2.predicted time is too high

60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 ree

gions]

60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time::

0.00 ms]

由於期望gc執行時間短,預期時間太長,只會掃很少的old區region,甚至可能比min region還少,當遇到連續的高代價region,即使是100%活躍的region也要拷貝,執行時間更長,下次預期時間更長,掃的region數更少,進入惡性循環。單次mixed gc釋放不了多少內存,gc時間佔比越來越高,有zk超時風險。

本質上,這是設置期望時間太短反而造成暫停時間更長,需要放寬期望gc執行時間,減少young 區最小值,以增大回收old區的可用時間。降低-XX:G1OldCSetRegionThresholdPercent比例以降低預期時間。內存使用上,讓不可回收內存比例低一些,避免高存活比例region連續出現的概率,即增大堆內存,增大old區回收閾值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低寫放大,降低磁碟讀寫IO,增大L1緩存可以提高讀緩存命中率。所以這不單單是gc自己的問題,要系統性綜合考慮,確定系統的瓶頸究竟是什麼,優化哪個問題更重要。

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytee

s (15.00 %), threshold: 15.00 %]

61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time::

0.00 ms]

到達垃圾保留比例,最後一次mixed gc只會掃很少的region,如果正好都是昂貴的region,則拷貝代價很高。

運氣不好的話,這幾乎是不可避免的。調大-XX:G1OldCSetRegionThresholdPercent理論上可以讓最後一次掃region的平均數量變大,但會造成predicted time is too high更頻繁出現。增加堆內存上限和old區回收閾值,提高-XX:G1HeapWastePercent比例,可以更早結束垃圾mixed gc周期,最後一次掃描都是昂貴region的概率也降低了。調大-XX:G1MixedGCCountTarget 讓min region更少,可能每次回收量減少一次回收周期時間拉長,需要配合更高的垃圾浪費率和更低的-XX:G1MixedGCLiveThresholdPercent比例,達到快速清理的效果。

gc問題掛進程

gc問題的極端後果是進程掛掉。一般經驗認為,內存增加比釋放快,內存不足,full gc, oom,進程就掛了。

我遇到過多次gc引起進程掛掉,但目前還沒遇到過g1的oom,甚至都沒遇到過g1的full gc。這是因為regionserver內存模型young區升代比例很低,另外g1在惡劣條件下gc時間佔比很高,即使regionserver壓力很大,還沒到full gc,就gc時間佔比過高引起zk session超時退出了。下面舉兩個例子。

1.寫入過猛引起的進程掛

業務方補一年的數據,hadoop作業寫入過猛,還有熱點,flush一直排隊,compact一直排隊,甚至觸發hfile上限堵塞寫了。寫的p99一度飆升到30秒,young gc一次升old區很多,old區內存增長比正常快。結果還沒到old區觸發mixed gc,由於young gc達到了1秒2,3次,gc時間佔比一度超過了95%,開始出現zk session超時,regionserver自動退出。

可以調大region hfile數上限來緩解,但治標不治本。需要控制用戶寫入,加quota來限制。

2.參數問題導致mixed gc連續出現長時間暫停

regionserver有一定壓力,在承受範圍內而進程掛了。這是由於參數設置有問題,由於期待暫停時間過低掃的region數不夠多,又都是不可回收region,暫停時間越來越長,幾次達到8,9秒暫停後,zk session超時退出。

這個按上面的redicted time is too high問題來優化即可。

優化方法論小結

1.-XX:ParallelGCThreads官方推薦是邏輯cpu核數的5/8,注意邏輯cpu核數是物理核的2倍,所以24核可以開到32,young和mixed gc確實變快了。

2.參數調整確保每輪mixed gc的max region數是min region數的4倍以上,降低都是昂貴region的幾率。

3.適量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暫停時間。目的是留給掃描region充足時間,確保每輪mixed gc掃描的region數和期待的max region數相似。

4.如果不想young gc時間也同步變長,可以通過-XX:G1MaxNewSizePercent降低young區最大比例來控制young gc時間。

5.降低最小young 區比例,可以降低mixed gc時回收young 區的時間,從而增加掃描old區region時間,確保掃描更多region。

6.觸發mixed gc周期時,old區可回收內存比例越高,越不容易遇到連續昂貴ergion,回收越有效率。所以應該調大堆內存,調高mixed gc觸發閾值,控制不可回收內存比例(即memstore和L1 block cache)。

7.當前麵條件都滿足時,每次mixed gc周期可回收內存比例很高,每輪mixed gc掃描的region數幾倍於min region有充足的region挑選不昂貴ergion,可以調高-XX:G1HeapWastePercent比例讓本輪mixed gc儘快結束,降低-XX:G1MixedGCLiveThresholdPercent優先回收活躍對象更少的region。

最終優化內容

-Xmx100g -Xms100g 50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent= 65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

推薦閱讀:

相关文章