top -H 查詢機器的cpu內存消耗情況,發現有一個Java進程的 cpu 利用率達到了 99%, 也就是說 跑滿了一個 核心, 線上配置為 4核32G, 相當於直接消耗了 四分之一的性能,感覺已經定位到了這個原因了。
其中有一個 進程 跑到了 99.9%
繼續追下去,剛開始以為這個線程是業務線程, 使用 jps 查出 主進程號碼。
之后使用 jstack pid | grep "cpu 99% 進程好的 16進制"
竟然發現這個線程是 g1 的垃圾收集器線程。
使用 jstack 查詢 jvm 垃圾收集情況, 大概 10s 進行一次 young gc
查看線程垃圾回收的日志信息,發現了一個很詭異的情況:
GC pause 很嚴重, 高的竟然有 1s 多,而這個階段是 stw 的,所以這個情況很不正常
含義參考:
最后發現 在 3月27號,算法組 新上了一個模型, 該模型 大概 2G, 並且不能回收, 默認 g1 預留 30% 作為 擔保分區, 所以在不停的 ygc 但是確沒有回收任何的對象,從而造成了 jvm 的阻塞延長,從而拉低了服務的 p90.
解決方案, 減小 jvm G1ReservePercent 預留空間 到 15%, 或者增大堆內存, 同時做了實驗,發現都是有效的 , cpu 利用率低了很多.
再出現這種情況以前,平時 20% , 到增加到 40%, 修復之后,cpu利用率也降低到了 20% p90, p95 也回落了下來。
發布后cpu 利用率圖:
進一步實驗
直接修改線上機器JVM參數,並做比較
實驗1: 使用 venus01
實驗:"-Xmx19g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500"
對照:-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=20
實驗2:使用venus02
實驗:-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30
對照:GC_PARAM="-Xmx20g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500"
實驗3:使用venus03
實驗:-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30
對照:"-Xmx20g -Xms20g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=20 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500"
實驗4:使用機器05
實驗:-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30
對照:-Xmx20g -Xms20g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=20
實驗5:使用機器07
實驗:"-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30"
對照:"-Xmx20g -Xms20g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30"
實驗6:使用機器6,8,9
實驗:"-Xmx16g -Xms16g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30"
對照:"-Xmx20g -Xms20g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=20 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500"
實驗7:使用機器1,4
venus04:-Xmx22g -Xms22g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=20 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500
venus01:-Xmx22g -Xms22g -XX:G1HeapRegionSize=2m -XX:G1ReservePercent=30 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=500
結論:
堆預留空間