1. 程式人生 > >記錄一次Metaspace擴容引發FGC的調優總結

記錄一次Metaspace擴容引發FGC的調優總結

開始之前

  在開始之前先記錄一個我碰到的jvm調優的坑。那就是…

為啥我配置到idea64exe.vmoptions中的引數沒有生效???

  由於之前一直是在mac上開發,本地開發時當需要優化jvm引數的時候直接去idea的安裝目錄裡修改idea.vmoptions就可以了,換到windows以後想當然的也這麼改,但是發現似乎我配置的引數並沒有生效, what‘s the f***?探索了一番終於發現了問題所在。

  windows是基於使用者登入的,idea會為每個使用者在當前使用者根目錄下建立一份配置資訊,所以在idea安裝目錄下修改idea.vmoptions是不生效的,如圖:

  不知道管理員使用者登入的話是不是就可以直接修改idea安裝目錄的ideaexe.vmoptions了,有一個很簡單的方法判斷你當前的idea專案使用的是哪裡的配置資訊。

發現問題

  ok,現在終於可以優化我們的jvm引數了,下面是一套我經常用的引數,在我以前開發的時候基本都是用這套引數,我也就直接複製到了idea64exe.vmoptions。

-Xms1024m
-Xmx1024m
-Xmn512m
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=256m
-XX:ReservedCodeCacheSize=512m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

  儲存重啟idea以後,開一個專案,嗯,沒啥感覺,一切正常。開兩個專案,嗯,怎麼有點卡呢。又開了一個專案,噩夢開始了,idea開始爆卡,點一下卡2s的那種。

定位原因

  1. jps:檢視idea platform的程序ID

  2. jstat -gcutil pid 3s:檢視程序垃圾回收情況

 

  從上圖可以看出,剛開始YGC和FGC都很健康,但隨著我開啟的專案越來越多,FGC開始飆升,直接導致了頁面明顯的卡頓,我試著關掉了幾個專案,只保留一個,FGC慢慢變小,分析上圖可以看出,M的佔比隨著FGC略有下降,從93.7%下降到93.2%左右,推測可能是Metaspace擴容導致的FGC。

確認原因

  通過jstat -gc pid 3s:可以檢視metaspace具體使用情況,下圖可以看出metaspace發生了擴容。

  更簡單的是,通過命令jvisualvm,開啟Java VisualVM檢視mataspace具體使用大小。

  果然,當我同時開啟多個專案時,metaspace發生了擴容,並且最終mataspace的使用量達到了接近250M,幾乎達到了上面我配置的引數-XX:MetaspaceSize=256m,於是我將上面配置中關於mataspace的引數刪掉。

-Xms1024m
-Xmx1024m
-Xmn512m
-XX:ReservedCodeCacheSize=512m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

   再次jstat -gcutil pid 3s檢視GC情況。

  這次FGC情況大有改善,隨著我開的專案越來越多,FGC也只是從6次漲到了8次。至此,問題基本就解決了,但如果只是這樣的話,我寫這篇筆記的意義就不大了,接下來的才是重點。

深入探究

  我嘗試重新添加了mataspace的引數,如下:

-Xms1024m
-Xmx1024m
-Xmn512m
-XX:MetaspaceSize=384m
-XX:MaxMetaspaceSize=384m
-XX:ReservedCodeCacheSize=512m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

 

  再次jstat -gcutil pid 3s檢視GC情況:

  上圖是我同時開了5個專案的GC情況,FGC只有1次,這才是讓我覺得困惑的地方,也就是說當我設定了MetaspaceSize=384m和不設定的時候,是有區別的,在網上看了很多關於mataspace的文章,我發現自己一直以來都沒有搞懂-XX:MetaspaceSize=256M的真正含義,這個配置的含義並不是初始化元資料區大小為256m,而僅僅表示的是觸發FGC的閾值,至於配置和不配置的區別,也就很明顯了,這個知識點在書上看到過但是沒放在心上,在實際碰到問題的時候才恍然大悟。

  Metaspace由於使用不斷擴容到-XX:MetaspaceSize引數指定的量,就會發生FGC;且之後每次Metaspace擴容都可能會發生FGC。

  我們知道Metapsace是在jdk8中引入的,之前叫permGen,就是我們所說的永久代。在jdk8以前,我們配置-XX:PermSize=256m,那就是說初始化一塊256m的記憶體作為永久代。但是mataspace就不一樣了,很明顯,mataspace要比perm高階的多。

總結

  • 如果沒有配置-XX:MetaspaceSize,那麼觸發FGC的閾值是21807104(約20.8m)。

  • 如果配置了-XX:MetaspaceSize,那麼觸發FGC的閾值就是配置的值。

  • 配置比不配置好,實際開發時,可以先開幾個專案檢視一下metaspace大概佔用多少記憶體,這個跟專案大小和複雜度有關,再根據實際情況配置-XX:MetaspaceSize。

檢視當前java程序,一般用來查詢程序ID(PID)

  • jps:檢視當前java程序及PID
  • jps -l:輸出主類或者jar的完全路徑名
  • jps -v:輸出jvm引數

動態檢視gc情況

  • jstat -gc pid 3s: 每隔3s列印當前pid程序的堆記憶體詳細資訊
  • jstat -gcutil pid 3s: 每隔3s列印當前pid程序的堆記憶體總體GC統計資訊

視覺化工具jvisualvm

  命令列直接輸入jvisualvm可開啟視覺化工具,動態檢視java程序內部詳細資訊

結合以上3種途徑,可以檢視java程序的詳細使用情況和GC情況。