java?Metaspace空間內(nèi)存超詳細(xì)分析
一、背景說(shuō)明
- 應(yīng)用框架:Spring Boot 2.3.12.RELEASE
- JDK 版本:Oracle JDK 1.8(如
jdk1.8.0_231) - 問(wèn)題現(xiàn)象:Metaspace 內(nèi)存占用超過(guò) 200MB,存在潛在內(nèi)存泄漏或類(lèi)加載異常風(fēng)險(xiǎn)
Metaspace 簡(jiǎn)介:
自 JDK 8 起,永久代(PermGen)被移除,取而代之的是 Metaspace。它用于存儲(chǔ)類(lèi)的元數(shù)據(jù)(如 Class、Method、Field 等),默認(rèn)使用本地內(nèi)存(Native Memory),理論上可動(dòng)態(tài)擴(kuò)展,但若類(lèi)加載器未正確卸載,仍可能導(dǎo)致 Metaspace OOM。
二、診斷命令與工具
1. 使用jcmd查看 Native Memory 分布(含 Metaspace)
前提:?jiǎn)?dòng) Java 應(yīng)用時(shí)需開(kāi)啟 Native Memory Tracking(NMT)
啟動(dòng)參數(shù)示例:
nohup /home/jdk/jdk1.8.0_231/bin/java \
-XX:+UnlockDiagnosticVMOptions \
-XX:NativeMemoryTracking=summary \
-Xmx${RUN_MAX_SIZE} \
-Xms${RUN_SIZE} \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=${DUMP_LOG} \
-XX:+PrintGC \
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=10M \
-Xloggc:safe_manage_gc.log \
-Dserver.port=${APP_PORT} \
-jar ${APP_NAME} >/dev/null 2>&1 &執(zhí)行 NMT 匯總分析:
/home/jdk/jdk1.8.0_231/bin/jcmd {pid} VM.native_memory summary將
{pid}替換為實(shí)際進(jìn)程 ID(可通過(guò)jps或ps獲?。?br />輸出中重點(diǎn)關(guān)注Class和Metaspace相關(guān)內(nèi)存使用量
?? 示例輸出片段:
Total: reserved=1500MB, committed=800MB
- Class (reserved=300MB, committed=250MB)
(classes #20000)2. 使用jcmd分析已加載的類(lèi)統(tǒng)計(jì)信息
命令:
jcmd {pid} GC.class_stats > /tmp/gc_class_stats.txt此命令輸出每個(gè)已加載類(lèi)的詳細(xì)信息,包括:
- 類(lèi)名
- 加載該類(lèi)的 ClassLoader
- 字節(jié)碼大小
- 是否活躍(是否被引用)
可通過(guò)分析
/tmp/gc_class_stats.txt定位異常加載的類(lèi)或重復(fù)加載的類(lèi)(如動(dòng)態(tài)代理、Groovy 腳本、熱部署等場(chǎng)景)
3. 本地開(kāi)發(fā)環(huán)境調(diào)試(IDEA)
在 Run/Debug Configurations → VM options 中添加以下參數(shù),便于觀察類(lèi)加載行為:
-XX:NativeMemoryTracking=summary -XX:MaxMetaspaceSize=256m -XX:+TraceClassLoading -XX:+TraceClassUnloading
-XX:+TraceClassLoading:打印每個(gè)被加載的類(lèi)-XX:+TraceClassUnloading:打印被卸載的類(lèi)(需配合 GC 觸發(fā))- 設(shè)置
MaxMetaspaceSize可加速?gòu)?fù)現(xiàn) Metaspace OOM 問(wèn)題
三、使用 Arthas 進(jìn)行運(yùn)行時(shí)分析
Arthas 是阿里開(kāi)源的 Java 診斷工具,適合生產(chǎn)環(huán)境實(shí)時(shí)排查。
1. 下載 Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
2. 啟動(dòng) Arthas
java -jar arthas-boot.jar
3. 選擇目標(biāo)進(jìn)程并分析類(lèi)加載情況
進(jìn)入 Arthas 控制臺(tái)后,輸入以下命令:
# 查看類(lèi)加載統(tǒng)計(jì) classloader # 查看某個(gè) ClassLoader 加載的所有類(lèi)(例如 Spring 的 LaunchedURLClassLoader) classloader -l # 查看特定類(lèi)是否被加載 sc com.example.YourClass # 查看類(lèi)加載器樹(shù)形結(jié)構(gòu)(有助于識(shí)別 ClassLoader 泄漏) classloader -t
重點(diǎn)關(guān)注:
- 是否存在大量動(dòng)態(tài)生成的類(lèi)(如 CGLIB、ASM、Javassist 生成的代理類(lèi))
- 是否有自定義 ClassLoader 未被回收
- Spring Boot DevTools、Groovy、腳本引擎等組件可能頻繁加載類(lèi)
四、常見(jiàn) Metaspace 膨脹原因
| 原因 | 說(shuō)明 | 排查建議 |
|---|---|---|
| 動(dòng)態(tài)類(lèi)生成過(guò)多 | 如 CGLIB 代理、Lambda 表達(dá)式、Groovy 腳本 | 檢查 GC.class_stats 中類(lèi)名是否含 $Proxy、$Lambda 等 |
| ClassLoader 泄漏 | Web 應(yīng)用熱部署、模塊化加載未清理 | 使用 Arthas classloader -t 查看 ClassLoader 實(shí)例數(shù) |
| 第三方庫(kù)缺陷 | 某些 ORM、規(guī)則引擎緩存類(lèi)元數(shù)據(jù) | 升級(jí)依賴(lài)或限制其使用范圍 |
| mybatis映射自然增長(zhǎng) | mybatis映射sql結(jié)果到實(shí)體類(lèi),MyBatis + 反射 → 大量 GeneratedMethodAccessor | 超過(guò)15次后,會(huì)生成GeneratedMethodAccessor,一個(gè)功能到極限后就不會(huì)再增長(zhǎng)數(shù)量,每個(gè)字段的get set方法都會(huì)生成 |
五、優(yōu)化建議
設(shè)置 Metaspace 上限(防止無(wú)限增長(zhǎng)):
-XX:MaxMetaspaceSize=256m
避免不必要的動(dòng)態(tài)類(lèi)生成:
- 減少運(yùn)行時(shí)字節(jié)碼操作
- 避免在循環(huán)中定義 Lambda 或匿名類(lèi)
定期分析類(lèi)加載情況:
- 生產(chǎn)環(huán)境定期執(zhí)行
jcmd pid GC.class_stats - 對(duì)比不同時(shí)間點(diǎn)的類(lèi)數(shù)量變化
- 生產(chǎn)環(huán)境定期執(zhí)行
如果服務(wù)本身非常大,功能非常多,class數(shù)量和metaspace會(huì)一直增長(zhǎng)到某個(gè)極限就不會(huì)再增長(zhǎng),結(jié)合IDEA本地測(cè)試即可
六、附錄:參考圖示

圖中顯示 Metaspace 已使用 210MB,且 Class 區(qū)域占比高,提示可能存在大量類(lèi)加載。
通過(guò)以上方法,可系統(tǒng)性地分析和優(yōu)化 Java 應(yīng)用的 Metaspace 內(nèi)存使用,有效預(yù)防 java.lang.OutOfMemoryError: Metaspace 異常。
總結(jié)
到此這篇關(guān)于java Metaspace空間內(nèi)存超詳細(xì)分析的文章就介紹到這了,更多相關(guān)java Metaspace空間內(nèi)存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot自動(dòng)重連Redis的實(shí)現(xiàn)方法
由于網(wǎng)絡(luò)或服務(wù)器問(wèn)題,Redis連接可能會(huì)斷開(kāi),導(dǎo)致應(yīng)用程序無(wú)法繼續(xù)正常工作,本文主要介紹了springboot自動(dòng)重連Redis的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
Java編程實(shí)現(xiàn)數(shù)組轉(zhuǎn)成list及l(fā)ist轉(zhuǎn)數(shù)組的方法
這篇文章主要介紹了Java編程實(shí)現(xiàn)數(shù)組轉(zhuǎn)成list及l(fā)ist轉(zhuǎn)數(shù)組的方法,結(jié)合實(shí)例形式較為詳細(xì)的總結(jié)分析了java實(shí)現(xiàn)數(shù)組與list之間相互轉(zhuǎn)換的操作技巧,需要的朋友可以參考下2017-09-09
Spring Boot整合Swagger測(cè)試api構(gòu)建全紀(jì)錄
這篇文章主要給大家介紹了關(guān)于Spring Boot整合Swagger測(cè)試api構(gòu)建的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
基于線程的wait和notify使用,生產(chǎn)消費(fèi)案例
這篇文章主要介紹了基于線程的wait和notify使用,生產(chǎn)消費(fèi)案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java如何獲取發(fā)送請(qǐng)求的電腦的IP地址
文章介紹了如何通過(guò)HttpServletRequest獲取客戶端IP地址,特別是當(dāng)客戶端通過(guò)代理訪問(wèn)時(shí),如何使用x-forwarded-for頭來(lái)獲取真實(shí)的IP地址2024-11-11
詳解Elasticsearch如何實(shí)現(xiàn)簡(jiǎn)單的腳本排序
Elasticsearch?是位于?Elastic?Stack?核心的分布式搜索和分析引擎,可以為所有類(lèi)型的數(shù)據(jù)提供近乎實(shí)時(shí)的搜索和分析。本文主要介紹了Elasticsearch如何實(shí)現(xiàn)簡(jiǎn)單的腳本排序,感興趣的可以了解一下2023-01-01

