用 JDK 17 真實生成可被 MAT 打開的堆快照練習文件
本文檔手把手教你用 JDK 17 生成真實的、MAT 可分析的堆快照文件.hprof并在 MAT 中完成一次完整的內(nèi)存泄漏分析練習。目錄一、前置準備二、編寫模擬內(nèi)存泄漏的 Java 程序三、編譯并運行四、生成堆快照三種方式五、用 MAT 打開并分析六、MAT 練習路線圖七、常見問題排查八、一鍵腳本一、前置準備1.1 確認 JDK 版本java-version預(yù)期輸出JDK 17 即可21 也行openjdk version 17.0.12 2024-07-16 OpenJDK Runtime Environment (build 17.0.127) OpenJDK 64-Bit Server VM (build 17.0.127, mixed mode)?? 不需要 JDK 17 特別版任何 JDK 11 都可以關(guān)鍵是有jmap和jcmd工具。1.2 下載 MAT參照之前的文檔下載與你 JDK 匹配的 MAT 版本JDK 版本推薦 MAT 版本下載地址JDK 17MAT 1.14.0https://download.eclipse.org/mat/1.14.0/rcp/JDK 21MAT 1.15.0 / 1.16.0https://www.eclipse.org/mat/downloads.php1.3 創(chuàng)建項目目錄mkdir-p~/mat-practicecd~/mat-practice二、編寫模擬內(nèi)存泄漏的 Java 程序2.1 完整源碼在項目目錄中創(chuàng)建LeakDemo.javaimportjava.util.*;importjava.util.concurrent.*;/** * 模擬內(nèi)存泄漏的練習程序 * * 包含三種經(jīng)典泄漏場景 * 1. 靜態(tài)緩存 Map 無淘汰機制最常見 * 2. ThreadLocal 未清理 * 3. 大對象byte[]持續(xù)分配 * * 運行方式 * javac LeakDemo.java * java -Xms512m -Xmx512m -XX:HeapDumpOnOutOfMemoryError \ * -XX:HeapDumpPath./leak.hprof LeakDemo */publicclassLeakDemo{// 場景1靜態(tài)緩存泄漏 // Spring 單例 Bean 中的 static Map只 put 不 removeprivatestaticfinalMapLong,byte[]productCachenewConcurrentHashMap();// 場景2ThreadLocal 泄漏 // 每個線程持有一個不斷增長的 List線程不退出 → 內(nèi)存泄漏privatestaticfinalThreadLocalListStringthreadLocalDataThreadLocal.withInitial(ArrayList::new);// 場景3大對象列表 // ArrayList 不斷 add 1MB 的 byte[]永不清理privatestaticfinalListbyte[]tempBuffersnewArrayList();// 控制程序運行privatestaticvolatilebooleanrunningtrue;publicstaticvoidmain(String[]args)throwsException{longpidProcessHandle.current().pid();System.out.println();System.out.println( LeakDemo - 內(nèi)存泄漏模擬程序);System.out.println( PID: pid);System.out.println();System.out.println();System.out.println(手動生成堆快照命令);System.out.println( jmap -dump:formatb,fileleak.hprof pid);System.out.println( 或);System.out.println( jcmd pid GC.heap_dump leak.hprof);System.out.println();System.out.println(等待 OOM 自動 dump或手動執(zhí)行上面的命令...);System.out.println();// 啟動監(jiān)控線程startMonitor();longcounter0;while(running){// --- 場景1緩存泄漏 ---// 每個對象 1MB不停塞入 MapproductCache.put(counter,newbyte[1024*1024]);// --- 場景2ThreadLocal 泄漏 ---// 每個線程的 List 不斷增長threadLocalData.get().add(leak-data-counter);// --- 場景3臨時緩沖區(qū)雖然叫臨時但永不清理---if(counter%100){tempBuffers.add(newbyte[1024*1024]);// 每10次加一個1MB}counter;// 打印進度if(counter%500){System.out.printf([%6d] cache%d, threadLocal%d, buffers%d%n,counter,productCache.size(),threadLocalData.get().size(),tempBuffers.size());}// 控制速度避免瞬間 OOM讓你有時間手動 dumpTimeUnit.MILLISECONDS.sleep(50);}}/** * 監(jiān)控線程每 5 秒打印堆內(nèi)存使用情況 */privatestaticvoidstartMonitor(){ThreadtnewThread(()-{RuntimertRuntime.getRuntime();while(running){longused(rt.totalMemory()-rt.freeMemory())/1024/1024;longmaxrt.maxMemory()/1024/1024;doublepct(double)used/max*100;System.out.printf([Monitor] 堆: %dMB/%dMB (%.0f%%)%s%n,used,max,pct,pct85? ?? 即將OOM!:);try{Thread.sleep(5000);}catch(InterruptedExceptionignored){}}},monitor);t.setDaemon(true);t.start();}}2.2 代碼說明場景代碼位置模擬的真實問題靜態(tài)緩存泄漏productCache電商商品緩存無淘汰Map 無限增長ThreadLocal 泄漏threadLocalDataWeb 容器線程池中的 ThreadLocal 未 remove大對象堆積tempBuffers集合只 add 不 clearbyte[] 占滿堆三、編譯并運行3.1 編譯cd~/mat-practice javac LeakDemo.java編譯成功后會生成LeakDemo.class文件。3.2 運行帶 OOM 自動 dump 參數(shù)java-Xms512m-Xmx512m\-XX:HeapDumpOnOutOfMemoryError\-XX:HeapDumpPath./leak.hprof\LeakDemo或者在pom.xml文件添加配置buildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationjvmArguments-Xms256m-Xmx256m-XX:MetaspaceSize128m-XX:MaxMetaspaceSize256m-XX:UseG1GC-XX:HeapDumpOnOutOfMemoryError-XX:HeapDumpPath./heapdump.hprof-Dfile.encodingUTF-8/jvmArguments/configuration/plugin/plugins/build參數(shù)解釋參數(shù)作用-Xms512m初始堆大小 512MB-Xmx512m最大堆大小 512MB故意設(shè)小快速觸發(fā) OOM-XX:HeapDumpOnOutOfMemoryErrorOOM 時自動生成堆快照-XX:HeapDumpPath./leak.hprof快照保存路徑3.3 預(yù)期運行效果 LeakDemo - 內(nèi)存泄漏模擬程序 PID: 12345 手動生成堆快照命令 jmap -dump:formatb,fileleak.hprof 12345 或 jcmd 12345 GC.heap_dump leak.hprof 等待 OOM 自動 dump或手動執(zhí)行上面的命令... [Monitor] 堆: 45MB/512MB (8%) [ 50] cache50, threadLocal50, buffers5 [ 100] cache100, threadLocal100, buffers10 [Monitor] 堆: 158MB/512MB (30%) [ 150] cache150, threadLocal150, buffers15 ... [Monitor] 堆: 450MB/512MB (87%) ?? 即將OOM! [ 480] cache480, threadLocal480, buffers48 [Monitor] 堆: 495MB/512MB (96%) [Monitor] 堆: 508MB/512MB (99%) ?? 即將OOM! java.lang.OutOfMemoryError: Java heap space Dumping heap to ./leak.hprof ... Heap dump file created [268435456 bytes in 2.345 secs]leak.hprof文件已自動生成這就是一個真實的、MAT 可分析的堆快照。四、生成堆快照三種方式方式一OOM 自動生成最簡單就是上面 3.2 的方式等程序自然 OOM 即可。適合練習和演示。方式二jmap 手動生成最常用 ?程序運行時另開一個cmd終端執(zhí)行# 1. 查找 PIDjps-l# 輸出示例12345 LeakDemo# 2. 生成堆快照jmap-dump:formatb,fileleak.hprofPID# 輸出Heap dump file created??關(guān)鍵參數(shù)說明formatb→ 生成二進制格式MAT 可識別不要加live參數(shù)jmap -dump:live,...會先觸發(fā) Full GC把所有泄漏對象清掉MAT 就分析不出問題了方式三jcmd 生成JDK 7 推薦# 查找 PIDjcmd# 生成堆快照jcmd12345GC.heap_dump leak.hprofjcmd是 Oracle 官方推薦的替代jmap的新工具功能更全。三種方式對比方式觸發(fā)條件優(yōu)點缺點OOM 自動堆滿了零操作自動生成服務(wù)已崩潰jmap隨時手動靈活不影響服務(wù)會觸發(fā) STW短暫停頓jcmd隨時手動官方推薦功能多同 jmap五、用 MAT 打開并分析5.1 打開堆快照啟動 MATFile→Open Heap Dump→ 選擇leak.hprof選擇Leak Suspects Report點擊Finish5.2 預(yù)期看到的分析結(jié)果MAT 會自動生成報告你應(yīng)該能看到類似以下內(nèi)容Leak Suspects Report:Dominator Tree支配樹Window→Open Dominator Tree按Retained Heap排序你會看到Path To GC Roots關(guān)鍵操作在 Dominator Tree 中右鍵ConcurrentHashMap選擇Path To GC Roots→exclude weak/soft references你會看到引用鏈③ Histogram直方圖Window→Histogram按Objects排序Class Name | Objects | Shallow Heap ----------------------------|---------|------------- byte[] | 528 | 480 MB ← 異常多的 byte[] java.lang.String | 480 | 38 MB java.util.concurrent.ConcurrentHashMap$Node | 480 | 30 MB④ Thread OverviewWindow→Thread Overview查看每個線程的內(nèi)存占用找到持有大量 ThreadLocal 數(shù)據(jù)的線程。六、MAT 練習路線圖打開堆快照后按以下順序練習由淺入深第 1 步看自動報告Leak Suspects標簽頁看 Problem Suspect 摘要點擊Details查看詳情理解 MAT 的自動分析邏輯第 2 步Histogram 分析Window→Histogram按對象數(shù)量排序 → 找異常多的類按 Shallow Heap 排序 → 找大對象右鍵類 →List objects→with incoming references第 3 步Dominator TreeWindow→Open Dominator Tree按 Retained Heap 排序找到占用內(nèi)存最大的對象理解 Shallow vs Retained 的區(qū)別第 4 步GC Roots 引用鏈核心技能右鍵對象 →Path To GC Roots→exclude weak references看清誰在引用這個對象理解為什么 GC 無法回收定位到具體的代碼變量名第 5 步OQL 查詢Window→OQL-- 查找所有 byte[] 且大小超過 1MB 的SELECT*FROMbyte[]WHERElength1048576-- 查找 String 對象SELECT*FROMjava.lang.String-- 查找 ArrayList 實例SELECT*FROMjava.util.ArrayListWHERElength100第 6 步對比兩次快照進階程序運行 30 秒時 dump 一次 →dump1.hprof程序運行 60 秒時 dump 一次 →dump2.hprofMAT 中打開dump2.hprof→Compare Basket→Add再打開dump1.hprof→Compare Basket→Add點擊Compare→ 查看對象增長趨勢七、常見問題排查7.1 MAT 打開報錯 “Unknown HPROF Version”原因文件不是標準 HPROF 格式可能是文本文件或偽造的。排查# 檢查文件頭前 4 個字節(jié)應(yīng)該是 JAVAxxd leak.hprof|head-1# 正確輸出00000000: 4a41 5641 2050 524f 4649 4c45 2031 2e30# (JAVA PROFILE 1.0)# 檢查文件大小ls-lhleak.hprof# 應(yīng)該 100MB512MB 堆大概產(chǎn)生 200~400MB 的 hprof解決用jmap -dump:formatb,...或jcmd GC.heap_dump重新生成。7.2 MAT 顯示 “No leak suspect was found”原因生成快照時加了live參數(shù)觸發(fā)了 Full GC泄漏對象被清掉了。錯誤命令jmap -dump:live,formatb,fileleak.hprofpid?正確命令jmap-dump:formatb,fileleak.hprofpid?7.3 MAT 打開時報 OOM原因MAT 自身內(nèi)存不足無法解析大文件。解決編輯MemoryAnalyzer.ini-vmargs -Xms2g -Xmx4g ← 調(diào)大這個建議設(shè)為 hprof 文件大小的 1.5~2 倍 -XX:UseG1GC7.4 生成的 hprof 文件太小只有幾 MB原因程序還沒來得及分配大量對象就退出了或者堆設(shè)太大、還沒填滿。解決減小堆-Xms256m -Xmx256m更快 OOM加快速度去掉Thread.sleep(50)確保程序真的在運行看控制臺輸出7.5 jmap 執(zhí)行卡住或報 “Unable to attach”原因PID 不對或權(quán)限不足或容器環(huán)境限制。排查# 確認 PIDjps-l# 確認是同一個用戶ps-ppid-ouser,pid,cmd# 容器環(huán)境用 jcmdjcmdpidGC.heap_dump leak.hprof八、一鍵腳本8.1 Linux/Mac 一鍵腳本run.sh#!/bin/bashset-eechoecho MAT 堆快照練習 - 一鍵運行echoecho# 編譯echo[1/3] 編譯...javac LeakDemo.javaecho? 編譯成功echo# 創(chuàng)建輸出目錄mkdir-pdumps# 運行后臺echo[2/3] 啟動 LeakDemo后臺運行...java\-Xms512m-Xmx512m\-XX:HeapDumpOnOutOfMemoryError\-XX:HeapDumpPath./dumps/leak_auto.hprof\-XX:PrintGCDetails\-Xloggc:./dumps/gc.log\LeakDemoPID$!echo? 進程已啟動PID$PIDecho# 等待 30 秒讓程序分配一些內(nèi)存echo[3/3] 等待 30 秒后手動生成堆快照...sleep30# 手動 dump不帶 live保留泄漏對象echo 正在生成堆快照...jmap-dump:formatb,file./dumps/leak_manual.hprof$PIDecho? 堆快照已保存到 ./dumps/leak_manual.hprofecho# 打印文件信息echo 生成的文件ls-lhdumps/echoecho 用 MAT 打開 ./dumps/leak_manual.hprof 開始練習echo# 等待用戶按回車后殺掉進程read-p按回車鍵停止 LeakDemo...kill$PID2/dev/nullecho? 進程已停止8.2 Windows 一鍵腳本run.batecho off echo echo MAT 堆快照練習 - 一鍵運行 echo echo. echo [1/3] 編譯... javac LeakDemo.java if errorlevel 1 ( echo ? 編譯失敗請檢查 JDK 是否安裝 pause exit /b 1 ) echo ? 編譯成功 echo. mkdir dumps 2nul echo [2/3] 啟動 LeakDemo后臺運行... start /b java -Xms512m -Xmx512m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./dumps/leak_auto.hprof LeakDemo echo ? 進程已啟動 echo. echo [3/3] 等待 30 秒... timeout /t 30 /nobreak nul echo 查找 PID 并生成堆快照... for /f tokens1 %%i in (jps -l ^| findstr LeakDemo) do ( echo PID: %%i jmap -dump:formatb,file./dumps/leak_manual.hprof %%i ) echo ? 堆快照已保存到 .\dumps\leak_manual.hprof echo. echo 生成的文件 dir dumps echo. echo 用 MAT 打開 .\dumps\leak_manual.hprof 開始練習 echo. pause九、總結(jié)核心要點堆快照必須從真實運行的 JVM 中導出用jmap/jcmd/-XX:HeapDumpOnOutOfMemoryError不要加live參數(shù)否則 Full GC 會把泄漏對象清掉formatb必須有確保是二進制格式MAT 的-Xmx要夠大建議設(shè)為 hprof 文件大小的 1.5~2 倍分析三板斧Histogram → Dominator Tree → Path To GC Roots完整命令速查# 編譯javac LeakDemo.java# 運行OOM 自動 dumpjava-Xms512m-Xmx512m\-XX:HeapDumpOnOutOfMemoryError\-XX:HeapDumpPath./leak.hprof\LeakDemo# 查找 PIDjps-l# 手動 dump推薦jmap-dump:formatb,fileleak.hprofpid# 或 jcmdjcmdpidGC.heap_dump leak.hprof# 驗證文件格式xxd leak.hprof|head-1# 應(yīng)看到4a41 5641 2050 524f 4649 4c45JAVA PROFILE練習建議先用小堆-Xmx256m快速觸發(fā) OOM 自動生成快照再用jmap手動在不同時機生成多份快照做對比分析。掌握這套流程后線上遇到真實 OOM 你就能從容應(yīng)對。附錄JVM 參數(shù)速查表參數(shù)作用推薦值-Xms初始堆大小與-Xmx一致-Xmx最大堆大小根據(jù)物理內(nèi)存設(shè)置-XX:HeapDumpOnOutOfMemoryErrorOOM 時自動 dump所有環(huán)境必開-XX:HeapDumpPathdump 文件路徑確保目錄存在且有寫權(quán)限-XX:PrintGCDetails打印 GC 詳情排查必備-XloggcGC 日志路徑/data/logs/gc.log-XX:DisableExplicitGC禁止 System.gc()生產(chǎn)環(huán)境建議開啟-XX:MaxDirectMemorySize限制堆外內(nèi)存根據(jù) NIO 使用量設(shè)置

相關(guān)新聞

電商商家獲客難獲客貴解決方案:平臺賣家如何借助 BBWEYY 打造第二獲客入口,含零代碼SAAS、AI編程、源碼定制交付

電商商家獲客難獲客貴解決方案:平臺賣家如何借助 BBWEYY 打造第二獲客入口,含零代碼SAAS、AI編程、源碼定制交付

平臺賣家如何借助 BBWEYY 打造第二獲客入口 摘要 在平臺流量競爭持續(xù)升級、廣告成本不斷上升的背景下,越來越多電商平臺商家面臨站內(nèi)獲客貴、站外獲客弱、客戶沉淀難的現(xiàn)實問題。本文圍繞電商平臺商家的增長困境展開,重點討論 BBWEYY 小程序與 GEO 服務(wù)…

2026/7/29 15:17:17 閱讀更多
三只貓課后作業(yè)3

三只貓課后作業(yè)3

說明 下面的思考以及答案,都是本人憑經(jīng)驗輸出的,對于 AI,我的經(jīng)驗很不足的,主要是起到一個記錄作用,以后熟悉這塊之后再回過頭來看看自己的來時路。 作者有一句話,值得在平時工作中使用:要思考a…

2026/7/29 15:17:17 閱讀更多
企業(yè)架構(gòu)管理軟件和畫架構(gòu)圖工具有什么區(qū)別?

企業(yè)架構(gòu)管理軟件和畫架構(gòu)圖工具有什么區(qū)別?

畫架構(gòu)圖工具解決“這張圖怎么畫”,企業(yè)架構(gòu)管理軟件解決“對象、關(guān)系和治理過程怎么長期維護”。一次方案討論用 Visio、ProcessOn 或?qū)I(yè)建模工具通常夠用;當同一對象要跨視圖復用,多部門共同維護,系統(tǒng)變更還要做影響分析和評審…

2026/7/29 16:27:24 閱讀更多
上市公司投資者情緒數(shù)據(jù)分析與應(yīng)用指南

上市公司投資者情緒數(shù)據(jù)分析與應(yīng)用指南

1. 項目背景與數(shù)據(jù)價值 2007-2024年上市公司投資者情緒數(shù)據(jù),是一份橫跨中國資本市場18年發(fā)展歷程的珍貴數(shù)據(jù)集。作為二級市場研究的"情緒溫度計",這類數(shù)據(jù)能直觀反映投資者對上市公司的集體心理預(yù)期變化。我在量化投資領(lǐng)域工作12年&#xff0c…

2026/7/29 16:27:24 閱讀更多
AI制度文檔編寫不是寫作文!用NLP+ISO/IEC 23053雙引擎驅(qū)動的12項結(jié)構(gòu)化校驗清單

AI制度文檔編寫不是寫作文!用NLP+ISO/IEC 23053雙引擎驅(qū)動的12項結(jié)構(gòu)化校驗清單

更多請點擊: https://intelliparadigm.com 第一章:AI制度文檔編寫不是寫作文!用NLPISO/IEC 23053雙引擎驅(qū)動的12項結(jié)構(gòu)化校驗清單 AI制度文檔的本質(zhì)是可執(zhí)行、可審計、可驗證的治理契約,而非文學性表達。將自然語言處理&#xf…

2026/7/29 16:27:24 閱讀更多
HarmonyOS 應(yīng)用開發(fā)《掌上英語》第60篇:應(yīng)用啟動優(yōu)化——從 Ability 創(chuàng)建到首頁首屏渲染

HarmonyOS 應(yīng)用開發(fā)《掌上英語》第60篇:應(yīng)用啟動優(yōu)化——從 Ability 創(chuàng)建到首頁首屏渲染

應(yīng)用啟動優(yōu)化——從 Ability 創(chuàng)建到首頁首屏渲染一、啟動過程的三個階段 HarmonyOS 應(yīng)用的啟動過程可以分為三個階段: Ability 創(chuàng)建階段:從用戶點擊應(yīng)用圖標到 onCreate 被調(diào)用窗口創(chuàng)建階段:從 onWindowStageCreate 到首幀內(nèi)容加載首屏渲染階…

2026/7/29 16:27:24 閱讀更多
面試官大笑:“一個任務(wù)拆給 5 個 Subagent 并行跑,不比 1 個快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個任務(wù)拆給 5 個 Subagent 并行跑,不比 1 個快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個月,我在重構(gòu) AlgoMooc 網(wǎng)站過程中,發(fā)現(xiàn)一個問題:在 Claude Code 里把一個任務(wù)拆給 5 個 Subagent 并行跑,結(jié)果可能比 1 個 agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過來的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號與動畫渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號與動畫渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂應(yīng)用,模擬了真實擲骰子的過程。應(yīng)用投擲兩個骰子(六面標準骰),使用 Unicode 骰面符號直觀展示每個骰子的點數(shù),并伴有快速滾動的動畫效果?!?/p>

2026/7/29 0:15:24 閱讀更多