記一次簡單的服務啟動GC優化
- 前言
- 症狀
- 優化
-
- Full GC
- Young GC
- 總結
前言
今天上線一個項目的時候在日志裡發現項目啟動的時候頻繁GC,花了點時間分析了一下并且調整了一下JVM參數。
症狀
項目啟動
java -server -Xmx1024m -XX:MaxDirectMemorySize=512M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.txt -jar xxxxxxxx.jar
gc.txt
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR5UMFRVT3VkaNBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwADNxIDN0kTMzAjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
好家夥,一啟動就來9次 Young GC ,2次 Full GC。
現在先用jinfo看看配置參數
注意幾個重要參數:
-XX:InitialHeapSize=62914560 //堆初始值
-XX:MaxHeapSize=1073741824 //堆最大值
-MaxDirectMemorySize=536870912 //虛拟機外最大記憶體
優化
Full GC
先來分析這兩次Full GC以及它們前面的Young GC
2.536: [GC (Metadata GC Threshold) [PSYoungGen: 19883K->3584K(128512K)] 27143K->12014K(169472K), 0.0158155 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
2.552: [Full GC (Metadata GC Threshold) [PSYoungGen: 3584K->0K(128512K)] [ParOldGen: 8430K->8782K(32256K)] 12014K->8782K(160768K), [Metaspace: 20409K->20409K(1069056K)], 0.0844666 secs] [Times: user=0.16 sys=0.00, real=0.09 secs]
4.965: [GC (Metadata GC Threshold) [PSYoungGen: 109848K->8697K(247296K)] 119422K->19608K(279552K), 0.0278398 secs] [Times: user=0.03 sys=0.01, real=0.03 secs]
4.993: [Full GC (Metadata GC Threshold) [PSYoungGen: 8697K->0K(247296K)] [ParOldGen: 10910K->14513K(47104K)] 19608K->14513K(294400K), [Metaspace: 33512K->33512K(1081344K)], 0.1136122 secs] [Times: user=0.20 sys=0.00, real=0.11 secs]
可以看到兩次Full GC都是由于Metadata GC Threshold造成的。我這裡用的是JDK8,參數裡沒有明确指定metaspace的初始值和上限,這個時候初始值應該是預設的 21M 。問題不大,那我就把它的初始值和上限調大一點吧,調到64M。
措施:
追加元空間初始值
java -server -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64m -Xmx1024m -XX:MaxDirectMemorySize=512M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.txt -jar xxxxxxxx.jar
這個時候再檢視gc.txt
可以看到已經沒有Full GC了,不過還是有8次Young GC。
Young GC
其實從我的啟動參數裡面就可以看到,堆的容量我隻指定了最大值(-Xmx1024m),并沒有指定初始值。是以可以在配置參數裡面看到 -XX:InitialHeapSize=62082048,即堆的初始容量隻有60m左右。
而JVM預設的新生代和老年代空間占比為 1 : 3,是以新生代在一開始的時候隻有15m的記憶體空間,是以JVM一開始在15m的時候就發生了Young GC。
是以現在要追加新生代和堆的初始值,新生代、老年代的空間占比我就不調了,使用預設的 1 : 3 即可,而堆的初始值調成1024m,這樣新生代的初始值就是256m了。
措施:
追加堆的初始值
java -server -Xms1024m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64m -Xmx1024m -XX:MaxDirectMemorySize=512M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.txt -jar xxxxxxxx.jar
現在再來看看gc.txt
可以看到隻有兩次Young GC了,這兩次Young GC是在新生代對象準備超過256m的時候發生的……
好家夥,既然如此,那我也不講武德了,我現在就把堆初始值和最大值都調成100G。
好吧,公司窮,隻能買得起4G的伺服器。
總結
這隻是一次很簡單的JVM啟動參數優化,平時啟動應用的時候還是得多注意注意堆的初始值和最大值配置,元空間這部分也不能忽略了。