1. 背景
線上某一個(gè)服務(wù)雖然沒有Full GC, 但是Young GC耗時(shí)一直居高不下,更重要的是Olden區(qū)的最大使用量與日遞增。集群機(jī)器配置是8C16G, 使用的是G1垃圾回收器, 目標(biāo)停頓時(shí)間是200ms, Xmx和Xms配置是12.8G(機(jī)器內(nèi)存的80%)。
Young GC次數(shù)與耗時(shí)如圖1所示,Eden、Survivor、老年代使用情況如圖2、3、4所示,為展示效果,時(shí)間段截取某次發(fā)布后的多天,可以明顯地看到老年代的最大使用量一直在遞增。




2. 排查與優(yōu)化
剛開始懷疑是有內(nèi)存泄露,但是通過MAT排查沒有發(fā)現(xiàn)問題,從老年代使用情況來看,不斷地有對象進(jìn)入老年代。在排除其他可能后,將懷疑的目光聚集在Region Size。該服務(wù)是一個(gè)方案排序服務(wù),報(bào)文比較大,幾兆到十幾兆不等。
我們知道G1垃圾收集器與之前垃圾收集器最大的不同就是化整為零,將內(nèi)存區(qū)域分成多個(gè)Region,每個(gè)Region可能是E、S、O、H的一種,如圖5所示。

其中H代表Humongous,表示這些Region存儲的是巨大對象(humongous object,H-obj),即大小大于等于region一半的對象。H-obj有如下幾個(gè)特征:
- H-obj直接分配到老年代,防止被反復(fù)拷貝移動(dòng)。
- H-obj在global concurrent marking階段的cleanup 和 full GC階段回收。
- 在分配H-obj之前先檢查是否超過 initiating heap occupancy percent和the marking threshold, 如果超過的話,就啟動(dòng)global concurrent marking,為的是提早回收,防止 evacuation failures 和 full GC。
Region size通過heapSize/2048計(jì)算得到,也可以通過參數(shù)-XX:G1HeapRegionSize=32m指定(其中32m指定region size為32M),Region size必須是2的指數(shù),取值范圍從1M到32M。針對于12.8G的堆內(nèi)存,Region Size是4M, 前面提到內(nèi)部有很多對象的大小為幾兆到十幾兆,遠(yuǎn)大于4M的一半,因此這些對象會被分配到H區(qū),造成老年代的使用不斷增加。
將Region大小調(diào)到16M或者32M時(shí),GC情況會有明顯的好轉(zhuǎn),當(dāng)region size 為32M時(shí)(16M效果近似),Young GC次數(shù)與耗時(shí)如圖6所示,Eden、Survivor、老年代使用情況如圖7、8、9所示。
- Young GC次數(shù):調(diào)整前每分鐘平均10次左右,調(diào)整后為5次左右
- Young GC耗時(shí):調(diào)整前平均耗時(shí)400ms,調(diào)整后50ms
- Eden使用情況: 調(diào)整前平均2GB,調(diào)整后3.3GB
- Survivor使用情況:調(diào)整前24MiB,調(diào)整后50MiB
- 老年代使用情況: 調(diào)整前不斷增加,調(diào)整后穩(wěn)定在295MiB



