JDK1.8應用啟動出現fullgc問題解決方案
一、問題現象
今天A系統上線時,B系統報了可用率問題。經檢視日誌,發現是B系統呼叫的A系統介面執行緒池被打滿,而且報警確實是剛剛上線完成所在的機器。
二、分析原因
通過分析A系統的ump發現該介面在這個時間點,效能出現了波動。見下圖:
該介面操作比較簡單,只操作了快取,一個hgetall操作,所以,懷疑是資源或者gc導致的。
1、檢查該伺服器GC情況。發現在這個時間點,發生了fullgc
但是發生fgc的時候,該伺服器jvm記憶體使用率很低。
發生fullgc,常見原因無外乎記憶體不夠用,或者程式呼叫System.gc導致。後者之前也檢查過,程式並沒有呼叫system.gc。
2、檢查該機器的jvm引數,發現XX:MaxPermSize=1024m,但是大家應該都知道jdk1.8已經使用metaspace替代了perm區[1],所以該引數是不會生效的。
-Xms4096m -Xmx4096m -XX:MaxPermSize=1024m
這臺伺服器使用的是預設的配置,Xmx[2]設定是4096m,但是metaspace沒有配置。列印預設引數的值,通過java -XX:+PrintFlagsFinal -version | grep MetaspaceSize檢視metaspacesize,可以看到預設值[3]為20m(21807104k)。
這裡有一個常見的誤區:
MetaspaceSize和jdk1.7的permsize含義是不同的,MetaspaceSize指的是“如果meta區使用達到了該值,會觸發fullgc,擴容”[4],而permsize指的是初始化perm區的大小。
通過jstat檢視meta區記憶體使用情況:
可以看到meta區,記憶體已經使用了86.19%(M=MU/MC),繼續檢視詳細情況:
3、隨著載入類的增多,我們的應用meta區遠超過20m的大小,所以會觸發fullgc,擴容metaspace。至此,問題已經很明確,因為jdk1.8的預設引數,導致需要擴容meta區,因此出現了fullgc。
三、解決方案
1、增加metaspace的配置,將metaspacesize和MaxMetaspaceSize配置設定成256m。避免meta區擴容。
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
2、修復之前及之後對比截圖
修復之前的,在預發環境重啟,發現發生了fullgc;修復之後,再次重啟,沒有gc發生。參見紅框部分兩個時間點。
註釋
[1] java8去掉perm用Metaspace來替代
java8徹底將永久代(PermGen)移除出了HotSpot JVM,將其原有的資料遷移至Java Heap或Metaspace。
在HotSpot JVM中,永久代中用於存放類和方法的元資料以及常量池,比如Class和Method。每當一個類初次被載入的時候,它的元資料都會放到永久代中。
java 8中PermGen為什麼被移出HotSpot JVM?兩個主要原因(詳見:JEP 122: Remove the Permanent Generation):
-
1、由於PermGen記憶體經常會溢位,引發惱人的 java.lang.OutOfMemoryError: PermGen
-
2、移除PermGen可以促進HotSpot JVM與JRockit VM的融合,因為JRockit沒有永久代。
根據上面的各種原因,PermGen最終被移除,方法區移至Metaspace,字串常量移至Java Heap。
java8開始把類的元資料放到本地堆記憶體(native heap)中,這一塊區域就叫Metaspace,中文名叫元空間。
[2] -Xms -Xmx -Xss
-
Xms 是指設定程式啟動時佔用記憶體大小。
-
Xmx 是指設定程式執行期間最大可佔用的記憶體大小。
-
Xss 是指設定每個執行緒的堆疊大小。
[3] metaspace size
metaspace使用的是本地記憶體,而不是堆記憶體,是沒有上限的。在java8中,XX:MaxMetaspaceSize確實是沒有上限的,最大容量與機器的記憶體有關;但是XX:MetaspaceSize是有一個預設值的,這個值大小根據不同的平臺在12M到20M浮動。
[4] metaspace fullgc
metaspace在空間不足時,會進行擴容,並逐漸達到設定的MetaspaceSize。Metaspace擴容到 -XX:MetaspaceSize 引數指定的量,就會發生FGC。如果配置了 -XX:MetaspaceSize,那麼觸發 FGC 的閾值就是配置的值。