目錄
一. 前言
二.?MAT 使用場景及主要解決問題
三. MAT 基礎概念
3.1. Heap Dump
3.2.?Shallow Heap
3.3.?Retained Set
3.4.?Retained Heap
3.5.?Dominator Tree
3.6.?OQL
3.7.?references
四.?MAT 功能概述
4.1.?內存分布
4.2.?對象間依賴
4.3.?對象狀態
4.4.?按條件檢索對象
4.5.?常見內存分析工具對比
五.?Quick Start 及使用技巧
5.1. Quick Start
5.2.?使用技巧及注意事項
一. 前言
? ? Memory Analyzer Tool(簡稱:MAT),是一款快速便捷且功能強大豐富的 JVM 堆內存離線分析工具。其通過展現 JVM 異常時所記錄的運行時堆轉儲快照(Heap Dump)狀態(正常運行時也可以做堆轉儲分析),幫助定位內存泄漏問題或優化大內存消耗邏輯。
二.?MAT 使用場景及主要解決問題
場景一:內存溢出,JVM 堆區或方法區放不下存活及待申請的對象。如:高峰期系統出現 OOM(Out of Memory)異常,需定位內存瓶頸點來指導優化。
場景二:內存泄漏,不會再使用的對象無法被垃圾回收器回收。如:系統運行一段時間后出現 Full GC,甚至周期性 OOM 后需人工重啟解決。
場景三:內存占用高。如:系統頻繁 GC ,需定位影響服務實時性、穩定性、吞吐能力的原因。
三. MAT 基礎概念
3.1. Heap Dump
? ? Heap Dump 是 Java 進程堆內存在一個時間點的快照,支持 HPROF 及 DTFJ 格式,前者由 Oracle 系列 JVM 生成,后者是 IBM 系列 JVM 生成。其內容主要包含以下幾類:
1. 所有對象的實例信息:對象所屬類名、基礎類型和引用類型的屬性等。
2. 所有類信息:類加載器、類名、繼承關系、靜態屬性等。
3. GC Root:GC Root 代表通過可達性分析來判定 JVM 對象是否存活的起始集合。JVM 采用追蹤式垃圾回收(Tracing GC)模式,從所有 GC Roots 出發通過引用關系可以關聯的對象就是存活的(且不可回收),其余的不可達的對象(Unreachable object:如果無法從 GC Root 找到一條引用路徑能到達某對象,則該對象為Unreachable object)可以回收。
4. 線程棧及局部變量:快照生成時刻的所有線程的線程棧幀,以及每個線程棧的局部變量。
3.2.?Shallow Heap
? ? Shallow Heap 代表一個對象結構自身所占用的內存大小,不包括其屬性引用對象所占的內存。如 java.util.ArrayList 對象的 Shallow Heap 包含8字節的對象頭、8字節的對象數組屬性 elementData 引用 、 4字節的 size 屬性、4字節的 modCount 屬性(從 AbstractList 繼承及對象頭占用內存大小),有的對象可能需要加對齊填充但 ArrayList 自身已對齊不需補充,注意不包含 elementData 具體數據占用的內存大小。
3.3.?Retained Set
? ? 一個對象的 Retained Set,指的是該對象被 GC 回收后,所有能被回收的對象集合(如下圖所示,G 的 Retain Set 只有 G 并不包含 H,原因是雖然 H 也被 G 引用,但由于 H 也被 F 引用 ,G 被垃圾回收時無法釋放 H);另外,當該對象無法被 GC 回收,則其 Retained set 也必然無法被 GC 回收。
3.4.?Retained Heap
? ? Retained Heap 是一個對象被 GC 回收后,可釋放的內存大小,等于釋放對象的 Retained Heap 中所有對象的 Shallow Heap 的和(如下圖所示,E 的 Retain Heap 就是 G 與 E 的 Shallow Heap 總和,同理不包含 H)。
3.5.?Dominator Tree
? ? 如果所有指向對象 Y 的路徑都經過對象 X,則 X 支配(dominate) Y(如下圖中,C、D 均支配 F,但 G 并不支配 H)。Dominator tree 是根據對象引用及支配關系生成的整體樹狀圖,支配樹清晰描述了對象間的依賴關系,下圖左的 Dominator tree 如下圖右下方支配樹示意圖所示。支配關系還有如下關系:
1. Dominator tree 中任一節點的子樹就是被該節點支配的節點集合,也就是其 Retain Set。
2. 如果 X 直接支配 Y,則 X 的所有支配節點均支配 Y。
3.6.?OQL
? ? OQL 是類似于 SQL 的 MAT 專用統一查詢語言,可以根據復雜的查詢條件對 dump 文件中的類或者對象等數據進行查詢篩選。
3.7.?references
? ? outgoing references、incoming references 可以直擊對象間依賴關系,MAT 也提供了鏈式快速操作。
1. outgoing references:對象引用的外部對象(注意不包含對象的基本類型屬性。基本屬性內容可在 inspector 查看)。
2. incoming references:直接引用了當前對象的對象,每個對象的 incoming references 可能有 0 到多個。
四.?MAT 功能概述
? ? MAT 的產品能力非常豐富,工作原理是對 dump 文件建立多種索引,并基于索引來實現:1. 內存分布;2. 對象間依賴(如實體對象引用關系、線程引用關系、ClassLoader引用關系等);3. 對象狀態(內存占用量、字段屬性值等);4. 條件檢索(OQL、正則匹配查詢等)。這四大核心功能,并通過可視化展現輔助 Developer 精細化了解 JVM 堆內存全貌。
4.1.?內存分布
全局概覽信息:堆內存大小、對象個數、類的個數、類加載器的個數、GC root 個數、線程概況等全局統計信息。
Dominator tree:按對象的 Retain Heap 排序,也支持按多個維度聚類統計,最常用的功能之一。
Histogram:羅列每個類實例的內存占比,包括自身內存占用量(Shallow Heap)及支配對象的內存占用量(Retain Heap),支持按 package、class loader、super class、class 聚類統計,最常用的功能之一。
Leak Suspects:直擊引用鏈條上占用內存較多的可疑對象,可解決一些基礎問題,但復雜的問題往往幫助有限。
Top Consumers:展現哪些類、哪些 class loader、哪些 package 占用最高比例的內存。
4.2.?對象間依賴
References:提供對象的外部引用關系、被引用關系。通過任一對象的直接引用及間接引用詳情(主要是屬性值及內存占用),進而提供完善的依賴鏈路詳情。
Dominator tree:支持按對象的 Retain Heap 排序,并提供詳細的支配關系,結合 references 可以實現大對象快速關聯分析。
Thread overview:展現轉儲 dump 文件時線程棧幀等詳細狀態,也提供各線程的 Retain Heap 等關聯內存信息。
Path To GC Roots:提供任一對象到 GC Root 的鏈路詳情,幫助了解不能被 GC 回收的原因。
4.3.?對象狀態
最核心的是通過 inspector 面板提供對象的屬性信息、類繼承關系信息等數據,協助分析內存占用高與業務邏輯的關系。
集合狀態的檢測,如:通過 ArrayList 或數組的填充率定位空集合空數組造成的內存浪費、通過 HashMap 沖突率判定 hash 策略是否合理等。
4.4.?按條件檢索對象
OQL:提供一種類似于SQL的對象(類)級別統一結構化查詢語言。如:查找 size=0 且未使用過的 ArrayList: select * from java.util.ArrayList where size=0 and modCount=0;查找所有的String的 length 屬性的: select s.length from instanceof String s。
內存分布及對象間依賴的眾多功能,均支持按字符串檢索、按正則檢索等操作。
按虛擬內存地址尋址,根據對象的十六進制地址查找對象。
此外,為了便于記憶與回顧,整理了如下腦圖:
4.5.?常見內存分析工具對比
下圖中 Y 表示支持,N 表示不支持,時間截至發稿前。
產品功能 | MAT | JProfiler | Visual VM | jhat | jmap | hprof |
---|---|---|---|---|---|---|
對象關聯分析、深淺堆、GC ROOT、內存泄漏檢測、線程分析、提供自定義程序擴展擴展 | Y | N | N | N | N | N |
離線全局分析 | Y | N | Y | Y | N | N |
內存實時分配情況 | N | Y | Y | Y | Y | Y |
OQL | Y | N | Y | N | N | N |
內存分配堆棧、熱點比例 | N | Y | N | N | N | N |
堆外內存分析 | N | N | N | N | N | N |
注 1:Dump 文件包含快照被轉儲時刻的 Java 對象在堆內存中的分布情況,但快照只是瞬間的記錄,所以不包含對象在何時、在哪個方法中被分配這類信息。
注 2:一般堆外內存溢出排查可結合 gperftools 與 btrace 排查,本文不展開介紹。
五.?Quick Start 及使用技巧
5.1. Quick Start
1. 安裝 MAT:【下載鏈接】;也可直接集成到 Eclipse IDE中(路徑:Eclipse → Help → Eclipse Marketplace → 搜 “MAT”)。
2. 調節 MAT 堆內存大小:MAT 分析時也作為 Java 進程運行,如果有足夠的內存,建議至少分配 dump 文件大小 * 1.2 倍的內存給 MAT,這樣分析速度會比較快。方式是修改 MemoryAnalyer.ini文件,調整 Xmx 參數(Windows 可用搜索神器 everything 軟件查找并修改、MAC OS 一般在 /Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini,如找不到可用 Alfred 軟件查詢修改)。
3. 獲取堆快照 dump 文件(堆轉儲需要先執行 Full GC,線上服務使用時請注意影響),一般用三種方式:
1>. 使用 JDK 提供的 jmap 工具,命令是 jmap -dump:format=b,file=文件名 進程號。當進程接近僵死時,可以添加 -F 參數強制轉儲:jmap -F -dump:format=b,file=文件名 進程號。
2>. 本地運行的 Java 進程,直接在 MAT 使用 File → accquire heap dump 功能獲取。
3>. 啟動 Java 進程時配置JVM參數:-XX:-HeapDumpOnOutOfMemoryError,當發生 OOM 時無需人工干預會自動生成 dump文件。指定目錄用 -XX:HeapDumpPath=文件路徑來設置。
4. 分析 dump 文件:路徑是 File → Open Heap Dump ,然后 MAT 會建立索引并分析,dump 文件較大時耗時會很長。分析后 dump 文件所在目錄會有后綴為 index 的索引文件,也會有包含 HTML 格式的后綴為 zip 的文件。
5. 完成索引計算后,MAT 呈現概要視圖(Overview),包含三個部分:
1>. 全局概覽信息,堆內存大小、類數量、實例數量、Class Loader數量。
2>. Unreachable Object Histogram,展現轉儲快照時可被回收的對象信息(一般不需要關注,除非 GC 頻繁影響實時性的場景分析才用到)。
3>. Biggest Objects by Retained Size,展現經過統計過的哪幾個實例所關聯的對象占內存總和較高,以及具體占用的內存大小,一般相關代碼比較簡單情況下,往往可以直接分析具體的引用關系異常,如內存泄漏等。此外也包含了最大對象和鏈接支持繼續深入分析。
6. 如果代碼比較復雜,需要繼續使用 MAT 各種工具并結合業務代碼進一步分析內存異常的原因。最常用的幾項如下:?
1>. 查看堆整體情況的:Histogram、Dominator tree、Thread details等(各功能入口整理如下)
2>.?MAT 分析過的 Top Consumers 、Leak Suspects 等
5.2.?使用技巧及注意事項
1. 注意對運行進程的性能影響:Heap Dump 時會先進行 Full GC,另外為保證對象數據視圖一致,需要在安全點 Stop The World 暫停響應,線上服務進行務必注意性能影響。可以采取以下技巧減少影響:
1>. 先禁用入口流量,再執行 dump 動作。
2>. 選擇影響較小時 dump 內存。
3>. 使用腳本捕獲指定事件時 dump 內存。
2. Dump 文件及建立的索引文件可能較大,如果開發機配置不足無法分析,可在服務器先執行分析后,基于分析后的索引文件直接查看結果,另外也需要注意磁盤占用問題:
1>. 大文件分析方法:一般 dump 文件不高于分析機主存 1.2 倍可直接在開發機分析;若 dump 文件過大,可以使用 MAT 提供的腳本在配置高的高配機器先建立索引再直接展現索引分析結果(一般是 Linux 機器,可以使用 MAT 提供的腳本:./ParseHeapDump.sh $HEAPDUMP,堆信息有 unreachable 標記的垃圾對象,在 dump 時也保存了下來,默認不分析此部分數據,如需要在啟動腳本 ParseHeapDump.sh 中加入:-keep_unreachable_objects)。
2>. 如果不關注堆中不可達對象,使用“live”參數可以減小文件大小,命令是 jmap -dump:live,format=b,file=
3>. Dump 前主動手動執行一次 FULL GC ,去除無效對象進一步減少 dump 堆轉儲及建立索引的時間。
4>. Dump文件巨大,建立索引后發現主視圖中對象占用內存均較小,這是因為絕大部分對象未被 GC Roots 引用可釋放。
5>. Dump 時注意指定到空間較大的磁盤位置,避免打滿分區影響服務。
6>. 建立 dump 索引機器的磁盤空間需要足夠大,一般至少是 dump 文件的兩倍,因為生成的中間索引文件也較大,如下圖:
3. 其他
1>. JDK 版本問題:如遇“VMVersionMismatchException”,使用啟動目標進程的 JDK 版本即可。
2>. 部分核心功能主界面未展現,問題足夠復雜時需打開,如 MAT 默認不打開 inspector,如需根據對象數據值做業務分析,建議打開該視圖。
3>. 配置了 HeapDumpOnOutOfMemoryError 參數,但 OutOfMemoryError 時但沒有自動生成 dump 文件,可能原因有三個:
?? ?3.1>. 應用程序自行創建并拋出 OutOfMemoryError;
?? ?3.2>. 進程的其他資源(如線程)已用盡;
?? ?3.3>. C 代碼(如 JVM 源碼)中堆耗盡,這種可能由于不同的原因而出現,例如在交換空間不足的情況下,進程限制用盡或僅地址空間的限制,此時 dump 文件分析并無實質性幫助。