天天看點

Metaspace記憶體不足導緻FGC問題排查

事件回顧

清楚的記得是2020/7/25 14:34分左右,周六的下午,我還在公司苦逼的加班中,突然釘釘告警群裡出現大量應用OP的dubbo逾時調用、空指針異常,異常中間還有Metaspace元空間不足等異常:

o.a.c.f.l.ListenerContainer 98 [ERROR] Listener \(org.apache.curator.framework.recipes.cache.PathChildrenCache$3@7edb7fd5) threw an exception

java.lang.OutOfMemoryError: Metaspace

錯誤類型:【oom】

告警内容:2020-07-25 15:05:05:113 d5f54db7c1ca49ab85b9f54cde234bd1 c.c.d.l.w.DriverTraceWriterUtil 39 [ERROR] driver trace writer to file fail,ex:[{}]

java.lang.RuntimeException: by java.lang.ClassFormatError: Metaspace

at com.caocao.dc
           

複制

再緊接着,發現我們應用OP的伺服器大量FullGC,先一台發生,很快第二台開始FGC,第10台...

2020-07-25T15:10:50
應用:xxx
主機:xxx(
agentId: yyyy
發生FGC,共耗時:25012ms

2020-07-25T15:10:25
應用:xxxx
主機:xxxx
agentId: yyy
發生FGC,共耗時:4223ms
           

複制

涉及到對OP系統調用的各系統都在回報出現dubbo調用逾時,都在報錯中,我們通過pinpoint也發現應用頻繁發生了FGC:

Metaspace記憶體不足導緻FGC問題排查

1.png

Metaspace記憶體不足導緻FGC問題排查

2.png

上面我們大概可以判斷出來,是由于Metaspace元空間不足,出現記憶體溢出,導緻jvm頻繁觸發full GC,為了保證業務正常,此時我們讓運維緊急重新開機了伺服器,通過重新開機伺服器,業務逐漸恢複正常,元空間使用量也降下來了。在發生FGC時讓運維dump記憶體了,後面會分析該檔案。

JVM參數

OP應用的生成JVM參數如下:

/usr/local/java/bin/java 
-server  #指定JVM的啟動模式是client模式還是server模式
-Xms4g   #初始化堆記憶體4G,堆記憶體最小值
-Xmx4g   #最大堆4g  
-Xmn2g   #年輕代2G,老年代大小=Xmx-Xmn
-Xss512k #每個線程的堆棧大小
-XX:MetaspaceSize=256m    #元空間的初始大小
-XX:MaxMetaspaceSize=512m #元空間最大值
-XX:-UseGCOverheadLimit   #預測是否要OOM了,提前抛出異常,防止OOM發生
-XX:+DisableExplicitGC    #禁用System.gc()
-XX:+UseConcMarkSweepGC   #指定老年代的收集算法使用CMS,會預設使用ParNew作為新生代收集器
-XX:+CMSParallelRemarkEnabled  #開啟并行标記,減少停頓時間
-XX:+UseCMSCompactAtFullCollection  #FULL GC時對老年代進行壓縮。CMS預設不會移動記憶體,是以容易産生碎片。增加該參數雖然會影響性能,但可以消除碎片
-XX:+UseFastAccessorMethods  #正确擷取方法的調用計數,以便VM可以更好地識别代碼中的熱點
-XX:+UseCMSInitiatingOccupancyOnly  #指定HotSpot VM總是使用-XX:CMSInitiatingOccupancyFraction的值作為老年代使用率限制來啟動CMS垃圾回收。如果沒有使用-XX:+UseCMSInitiatingOccupancyOnly,那麼HotSpot VM隻是利用CMSInitiatingOccupancyFraction啟s動第一次CMS垃圾回收,後面都是使用HotSpot VM自動計算出來的值
-XX:CMSInitiatingOccupancyFraction=70  #CMS垃圾收集器,老年代使用率達到70%時,觸發CMS垃圾回收
-XX:LargePageSizeInBytes=128m  #堆記憶體大頁的大小,大的記憶體分頁可以增強 CPU 的記憶體尋址能力,進而提升系統的性能
-Djava.awt.headless=true 
-Djava.net.preferIPv4Stack=true 
-Ddubbo.application.qos.port=12881 
-javaagent:/usr/local/pinpoint/pinpoint-bootstrap-1.6.0.jar 
-Dpinpoint.agentId=driver-op-...
-Dpinpoint.applicationName=OP
-Djava.ext.dirs=/usr/local/springboot/OP/lib:/usr/local/java/jre/lib/ext 
-XX:+HeapDumpOnOutOfMemoryError  #當堆記憶體空間溢出時輸出堆的記憶體快照,配合-XX:HeapDumpPath使用
-XX:HeapDumpPath=/home/admin     #當堆記憶體空間溢出時輸出堆的記憶體快照輸出目錄
-cp /usr/local/springboot/OP/conf:.:/usr/local/java/lib:/usr/local/java/jre/lib -jar /usr/local/springboot/OP/OP.jar
           

複制

由配置的JVM參數知道,指定了CMS為老年代的垃圾收集器,預設ParNew為新生代垃圾收集器,最大堆4g,老年代2g,年輕代2g,年輕中Eden區域和Survivor區域(From幸存區或To幸存區)的預設比例為8, 即設定survivor:eden=2:8(From:TO:eden=200MB:200MB:1600MB),元空間初始化大小256MB,最大值512MB,如果老年代空間使用率達到70%,會觸發CMS垃圾回收。由pinpoint上可以看出,元空間使用大概在770MB左右,超過了最大元空間值,導緻元空間記憶體不足,觸發FGC,這裡有個疑問,明明配置的最大512MB,為什麼使用了770MB,Metaspace還有一個區間是Klass Metaspace,由參數-XX:CompressedClassSpaceSize進行控制,JDK8的時候 Klass Metaspace預設是1G。

原因分析
  • MAT分析

    使用MAT打開dump檔案,點開

    Histogram

    柱狀圖,選擇

    java.lang.Class

    ,右擊選擇

    List objects

    ,選擇

    with incoming references(目前檢視的對象,被外部引用)

    ,檢視通過這個class建立的類資訊:
Metaspace記憶體不足導緻FGC問題排查
Metaspace記憶體不足導緻FGC問題排查

發現建立了大量

Proxy

類,右擊選中

Path To GC Roots

,選中

exclude all phantom/weak/soft etc.references

(排除虛引用/弱引用/軟引用等的引用鍊,被虛引用/弱引用/軟引用的對象可以直接被GC給回收,要看該對象否還存在Strong引用鍊,如果有,則說明存在記憶體洩漏):

Metaspace記憶體不足導緻FGC問題排查
Metaspace記憶體不足導緻FGC問題排查

發現

Proxy

類被

org.springframework.boot.loader.LaunchedURLClassLoader

強引用,導緻生成的

Proxy

類無法被解除安裝一直殘留在MetaSpace區造成記憶體洩漏。

  • 代碼分析

    上面分析出來生成

    Proxy

    類可能存在記憶體洩漏,代碼中會發現用動态代理建立

    Proxy

    類對象并放入WeakReference中,每次GC時該對象都會被回收,會重複建立

    Proxy

    類對象,而且類加載器不會被回收,導緻類不會被解除安裝。具體代碼參考了dubbo代碼

    com.alibaba.dubbo.common.bytecode.Proxy#getProxy(java.lang.ClassLoader, java.lang.Class<?>...)

  • 解決方法

    上層業務做緩存處理,不會重複建立

    Proxy

    對象。上線觀察優化前後5天内的元空間增長,的确效果比較明顯。
參考文章
  • https://www.jianshu.com/p/738b4f3bc44b
  • https://www.cnblogs.com/throwable/p/12216546.html
  • https://blog.csdn.net/a15939557197/article/details/90635460?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param