java.lang.OutOfMemoryError: Metaspace 的解決


j

談談1974 2021-02-25 17:41:13 124 收藏
分類專欄: JVM 隨筆 Java 基礎 文章標簽: java jvm Metaspace
版權
文章目錄
1. 起因
2. Metaspace 的使用機制
3. 解決方式
4. Metaspace 參數匯總
5. 總結
1. 起因
組內一個運營服務設計之初承載的業務數據量比較小,隨着服務功能逐漸擴展業務量快速增長,最近時常出問題,甚至在告警中報出了 java.lang.OutOfMemoryError: Metaspace 錯誤

我們都知道 Metaspace 是 Java 8 以后的方法區實現,主要存儲的就是JVM 加載到內存中的類相關數據,以及即時編譯的代碼等。很顯然,這個錯誤的原因大概率是加載到內存中的 class 占用的內存超過了 Metaspace 的限制

2. Metaspace 的使用機制
在解決這個錯誤之前,我們首先來了解下 Metaspace 的使用機制。通常 JVM 加載類時,會進行以下操作最終將類加載進內存:

通過一個類的全限定名來獲取其定義的二進制字節流
將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構,也就是存儲在 Metaspace 中
在Java堆中生成一個代表這個類的 java.lang.Class對象,作為對方法區中這些數據的訪問入口
當 Metaspace 空間不足的時候,會觸發 Full GC 卸載一些無用的類以便回收內存,但是如我們所知,類的卸載條件是很苛刻的(參考方法區垃圾回收),這樣就會發生一種情況,那就是即便進行了 Full GC,JVM 也無法回收到足夠的內存以存儲新加載的類,最終導致其占有的內存超過限制,只能報出 OOM 錯誤

Metaspace 空間不足觸發 Full GC 這個點只要多執行幾次 jstat -gcutil pid 命令,觀察 Full GC 的次數變化就能看出來,需注意 Full GC 非常影響程序效率,應該避免

3. 解決方式
通過以上分析,我們很容易就能想到解決方法:

Metaspace 空間不足,那么增大 Metaspace 的空間就可以了,這種方式簡單粗暴,但是高效
Metaspace 空間不足大概率是因為加載的類太多,而受限於苛刻的類卸載條件無法將其回收掉,那么最好的解決方式就是不加載那些不需要的類。基於這個思路,檢查服務是否加載了過多的類,將其找出來,去除掉多余的加載項。這種方式治標治本,但是比較耗時
最終出於效率考慮,我們選擇了增大 Metaspace 的空間的解決方法,以下是兩種處理方式:

在服務的啟動參數中將 MaxMetaspaceSize 最大元空間內存從 128m 增大到 256m,服務啟動后運行一段時間問題不再復現

-XX:MaxMetaspaceSize=256m
1
直接去掉 -XX:MaxMetaspaceSize 啟動參數,不限制 Metaspace 內存的大小。這種方式需要注意,假若機器物理內存不足,有可能會引起內存交換(swapping),嚴重拖累系統性能,還可能造成 native 內存分配失敗等問題

4. Metaspace 參數匯總
參數 含義
-XX:MetaspaceSize=N 初始化的 Metaspace 大小,該值越大觸發 Metaspace 區域 GC 的時機就越晚。隨着GC的到來,JVM 會根據實際情況調控 Metaspace 的大小,可能增加上限也可能降低
-XX:MaxMetaspaceSize=N 限制 Metaspace 內存的最大值,防止因為某些情況導致 Metaspace 無限使用本地內存,影響到其他程序
-XX:MinMetaspaceFreeRatio=N 當進行過 Metaspace 區域 GC 之后,JVM 會計算當前 Metaspace 的空閑空間比,如果空閑比小於這個參數,那么 JVM 將增大 Metaspace 的內存大小。該參數的默認值為40,也就是40%。設置這個參數可以控制 Metaspace 的增長的速度,太小的值會導致 Metaspace 增長緩慢,Metaspace 的使用逐漸趨於飽和,可能會影響之后類的加載。而太大的值會導致 Metaspace 增長過快,浪費內存
-XX:MaxMetasaceFreeRatio=N 當進行過 Metaspace 區域的GC之后, 會計算當前 Metaspace 的空閑空間比,如果空閑比大於這個參數,那么虛擬機會釋放 Metaspace 的部分空間。該參數的默認值為70,也就是70%
-XX:MaxMetaspaceExpansion=N Metaspace 增長時的最大幅度,參數的默認值為5452592B(大約為5MB)
-XX:MinMetaspaceExpansion=N Metaspace 增長時的最小幅度,該參數的默認值為340784B(大約為330KB)
5. 總結
Metaspace 是垃圾回收率比較低的 JVM 堆外內存區域,總結來說,其占用內存過多通常有以下兩種原因:

引用的 jar 包加載了很多 class
這種情況需要排查服務中引用的比較大的 jar 包,去除掉多余的加載項,對於那些不能去掉的,按照最小粒度去加載
動態生成類過多
Java 的許多框架會大量使用動態代理等技術動態生成類,使用這樣的方式創建對象可能會有大量動態類產生,元空間消耗會增加,這種情況似乎沒有好的解決方法
————————————————
版權聲明:本文為CSDN博主「談談1974」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_45505313/article/details/114089053


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM