JVM 內存分析工具 MAT 的深度講解與實踐——入門篇 1. MAT 工具簡介 2. MAT 功能概述及對比 3. Quick Start 及使用技巧

JVM 內存分析往往由團隊較資深的同學來做,本系列通過3篇文章,深度解析並幫助讀者全面深度掌握 MAT 的使用方法。即使沒有 JVM 內存分析的實踐經驗,也能快速成爲內存分析高手!

本系列共三篇文章如下,本文是第一篇入門篇:

《JVM 內存分析工具 MAT 的深度講解與實踐——入門篇》介紹 MAT 產品功能、基礎概念、與其他工具對比、Quick Start 指南。

《JVM 內存分析工具 MAT 的深度講解與實踐——進階篇》展開並詳細介紹 MAT 的核心功能,並在具體實戰場景下講解幫大家加深體會。

《JVM 內存分析工具 MAT 的深度講解與實踐——高階篇》總結複雜內存問題的系統性分析方法,並通過一個綜合案例提升大家的實戰能力。

1. MAT 工具簡介

MAT(全名:Memory Analyzer Tool),是一款快速便捷且功能強大豐富的 JVM 堆內存離線分析工具。其通過展現 JVM 異常時所記錄的運行時堆轉儲快照(Heap dump)狀態(正常運行時也可以做堆轉儲分析),幫助定位內存泄漏問題或優化大內存消耗邏輯。

1.1 MAT 使用場景及主要解決問題

場景一:內存溢出,JVM堆區或方法區放不下存活及待申請的對象。如:高峯期系統出現 OOM(Out of Memory)異常,需定位內存瓶頸點來指導優化。

場景二:內存泄漏,不會再使用的對象無法被垃圾回收器回收。如:系統運行一段時間後出現 Full GC,甚至週期性 OOM 後需人工重啓解決。

場景三:內存佔用高。如:系統頻繁 GC ,需定位影響服務實時性、穩定性、吞吐能力的原因。

1.2 基礎概念

1.2.1 Heap Dump

Heap Dump 是 Java 進程堆內存在一個時間點的快照,支持 HPROF 及 DTFJ 格式,前者由 Oracle 系列 JVM 生成,後者是 IBM 系列 JVM 生成。其內容主要包含以下幾類:

所有對象的實例信息:對象所屬類名、基礎類型和引用類型的屬性等。

所有類信息:類加載器、類名、繼承關係、靜態屬性等。

GC Root:GC Root 代表通過可達性分析來判定 JVM 對象是否存活的起始集合。JVM 採用追蹤式垃圾回收(Tracing GC)模式,從所有 GC Roots 出發通過引用關係可以關聯的對象就是存活的(且不可回收),其餘的不可達的對象(Unreachable object:如果無法從 GC Root 找到一條引用路徑能到達某對象,則該對象爲Unreachable object)可以回收。

線程棧及局部變量:快照生成時刻的所有線程的線程棧幀,以及每個線程棧的局部變量。

1.2.2 Shallow Heap

Shallow Heap 代表一個對象結構自身所佔用的內存大小,不包括其屬性引用對象所佔的內存。如 java.util.ArrayList 對象的 Shallow Heap 包含8字節的對象頭、8字節的對象數組屬性 elementData 引用 、 4字節的 size 屬性、4字節的 modCount 屬性(從 AbstractList 繼承及對象頭佔用內存大小),有的對象可能需要加對齊填充但 ArrayList 自身已對齊不需補充,注意不包含 elementData 具體數據佔用的內存大小。

1.2.3 Retained Set

一個對象的 Retained Set,指的是該對象被 GC 回收後,所有能被回收的對象集合(如下圖所示,G的 Retain Set 只有 G 並不包含 H,原因是雖然 H 也被 G 引用,但由於 H 也被 F 引用 ,G 被垃圾回收時無法釋放 H);另外,當該對象無法被 GC 回收,則其 Retained set 也必然無法被 GC 回收。

1.2.4 Retained Heap

Retained Heap 是一個對象被 GC 回收後,可釋放的內存大小,等於釋放對象的 Retained Heap 中所有對象的 Shallow Heap 的和(如下圖所示,E 的 Retain Heap 就是 G 與 E 的 Shallow Heap 總和,同理不包含 H)。

1.2.5 Dominator tree

如果所有指向對象 Y 的路徑都經過對象 X,則 X 支配(dominate) Y(如下圖中,C、D 均支配 F,但 G 並不支配 H)。Dominator tree 是根據對象引用及支配關係生成的整體樹狀圖,支配樹清晰描述了對象間的依賴關係,下圖左的 Dominator tree 如下圖右下方支配樹示意圖所示。支配關係還有如下關係:

Dominator tree 中任一節點的子樹就是被該節點支配的節點集合,也就是其 Retain Set。

如果 X 直接支配 Y,則 X 的所有支配節點均支配 Y。

1.2.6 OQL

OQL 是類似於 SQL 的 MAT 專用統一查詢語言,可以根據複雜的查詢條件對 dump 文件中的類或者對象等數據進行查詢篩選。

1.2.7 references

outgoing references、incoming references 可以直擊對象間依賴關係,MAT 也提供了鏈式快速操作。

outgoing references:對象引用的外部對象(注意不包含對象的基本類型屬性。基本屬性內容可在 inspector 查看)。

incoming references:直接引用了當前對象的對象,每個對象的 incoming references 可能有 0 到多個。

2. MAT 功能概述及對比

2.1 MAT 功能概述

注:MAT 的產品能力非常豐富,本文簡要總結產品特性幫大家瞭解全貌,在下一篇文章《JVM 內存分析實戰進階篇——核心功能及應用場景》中,會詳細展開介紹各項核心功能的場景、案例、最佳實踐等。

MAT 的工作原理是對 dump 文件建立多種索引,並基於索引來實現 [1]內存分佈、[2]對象間依賴(如實體對象引用關係、線程引用關係、ClassLoader引用關係等)、[3]對象狀態(內存佔用量、字段屬性值等)、[4]條件檢索(OQL、正則匹配查詢等)這四大核心功能,並通過可視化展現輔助 Developer 精細化了解 JVM 堆內存全貌。

2.1.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 佔用最高比例的內存。

2.1.2 對象間依賴

References:提供對象的外部引用關係、被引用關係。通過任一對象的直接引用及間接引用詳情(主要是屬性值及內存佔用),進而提供完善的依賴鏈路詳情。

Dominator tree:支持按對象的 Retain Heap 排序,並提供詳細的支配關係,結合 references 可以實現大對象快速關聯分析;

Thread overview:展現轉儲 dump 文件時線程棧幀等詳細狀態,也提供各線程的 Retain Heap 等關聯內存信息。

Path To GC Roots:提供任一對象到GC Root的鏈路詳情,幫助瞭解不能被 GC 回收的原因。

2.1.3 對象狀態

最核心的是通過 inspector 面板提供對象的屬性信息、類繼承關係信息等數據,協助分析內存佔用高與業務邏輯的關係。

集合狀態的檢測,如:通過 ArrayList 或數組的填充率定位空集合空數組造成的內存浪費、通過 HashMap 衝突率判定 hash 策略是否合理等。

2.1.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。

內存分佈及對象間依賴的衆多功能,均支持按字符串檢索、按正則檢索等操作。

按虛擬內存地址尋址,根據對象的十六進制地址查找對象。

此外,爲了便於記憶與回顧,整理了如下腦圖:

2.2 常見內存分析工具對比

下圖中 Y 表示支持,N 表示不支持,時間截至發稿前。

注 1:Dump 文件包含快照被轉儲時刻的 Java 對象 在堆內存中的分佈情況,但快照只是瞬間的記錄,所以不包含對象在何時、在哪個方法中被分配這類信息。

注 2:一般堆外內存溢出排查可結合 gperftools 與 btrace 排查,此類文章較多不展開介紹。

3. Quick Start 及使用技巧

3.1 Quick Start

注: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,線上服務使用時請注意影響),一般用三種方式:

使用 JDK 提供的 jmap 工具,命令是 jmap -dump:format=b,file=文件名 進程號。當進程接近僵死時,可以添加 -F 參數強制轉儲:jmap -F -dump:format=b,file=文件名 進程號。

本地運行的 Java 進程,直接在 MAT 使用 File → accquire heap dump 功能獲取。

啓動 Java 進程時配置JVM參數:-XX:-HeapDumpOnOutOfMemoryError,當發生 OOM 時無需人工干預會自動生成 dump文件。指定目錄用 -XX:HeapDumpPath=文件路徑 來設置。

4、分析 dump 文件:路徑是 File → Open Heap Dump ,然後 MAT 會建立索引並分析,dump 文件較大時耗時會很長。分析後 dump 文件所在目錄會有後綴爲 index 的索引文件,也會有包含 HTML 格式的後綴爲 zip 的文件。

5、完成索引計算後,MAT 呈現概要視圖(Overview),包含三個部分:

全局概覽信息,堆內存大小、類數量、實例數量、Class Loader數量。

Unreachable Object Histogram,展現轉儲快照時可被回收的對象信息(一般不需要關注,除非 GC 頻繁影響實時性的場景分析纔用到)

Biggest Objects by Retained Size,展現經過統計過的哪幾個實例所關聯的對象佔內存總和較高,以及具體佔用的內存大小,一般相關代碼比較簡單情況下,往往可以直接分析具體的引用關係異常,如內存泄漏等。此外也包含了最大對象和鏈接支持繼續深入分析。

6、如果代碼比較複雜,需要繼續使用 MAT 各種工具並結合業務代碼進一步分析內存異常的原因。最常用的幾項如下(具體案例、場景、使用方式在《JVM 內存分析工具 MAT 的深度講解與實踐——進階篇》詳細介紹):

查看堆整體情況的:Histogram、Dominator tree、Thread details等(各功能入口整理如下)

MAT 分析過的 Top Consumers 、Leak Suspects等

3.2 使用技巧及注意事項

1、注意對運行進程的性能影響:Heap dump 時會先進行 Full GC,另外爲保證對象數據視圖一致,需要在安全點 Stop The World 暫停響應,線上服務進行務必注意性能影響。可以採取以下技巧減少影響:

先禁用入口流量,再執行 dump 動作。

選擇影響較小時 dump 內存。

使用腳本捕獲指定事件時 dump 內存。

2、Dump 文件及建立的索引文件可能較大,如果開發機配置不足無法分析,可在服務器先執行分析後,基於分析後的索引文件直接查看結果,另外也需要注意磁盤佔用問題:

大文件分析方法:一般 dump 文件不高於分析機主存 1.2 倍可直接在開發機分析;若 dump 文件過大,可以使用 MAT 提供的腳本在配置高的高配機器先建立索引再直接展現索引分析結果(一般是 Linux 機器,可以使用 MAT 提供的腳本:./ParseHeapDump.sh $HEAPDUMP,堆信息有 unreachable 標記的垃圾對象,在 dump 時也保存了下來,默認不分析此部分數據,如需要在啓動腳本 ParseHeapDump.sh 中加入:-keep_unreachable_objects)。

如果不關注堆中不可達對象,使用“live”參數可以減小文件大小,命令是 jmap -dump:live,format=b,file=<dumpfile> <pid>

Dump 前主動手動執行一次 FULL GC ,去除無效對象進一步減少 dump 堆轉儲及建立索引的時間。

Dump文件巨大,建立索引後發現主視圖中對象佔用內存均較小,這是因爲絕大部分對象未被 GC Roots 引用可釋放。

Dump 時注意指定到空間較大的磁盤位置,避免打滿分區影響服務。

建立 dump 索引機器的磁盤空間需要足夠大,一般至少是 dump 文件的兩倍,因爲生成的中間索引文件也較大,如下圖:

3、其他

JDK 版本問題:如遇“VMVersionMismatchException”,使用啓動目標進程的 JDK 版本即可。

部分核心功能主界面未展現,問題足夠複雜時需打開,如 MAT 默認不打開 inspector,如需根據對象數據值做業務分析,建議打開該視圖。

配置了 HeapDumpOnOutOfMemoryError 參數,但 OutOfMemoryError 時但沒有自動生成 dump 文件,可能原因有三個:

應用程序自行創建並拋出 OutOfMemoryError

進程的其他資源(如線程)已用盡

C 代碼(如 JVM 源碼)中堆耗盡,這種可能由於不同的原因而出現,例如在交換空間不足的情況下,進程限制用盡或僅地址空間的限制,此時 dump 文件分析並無實質性幫助。

總結展望:至此本文講解了 MAT 實踐必備的初級內容,在下一篇《JVM 內存分析工具 MAT 的深度講解與實踐——進階篇》會展開並詳細介紹 MAT 豐富的核心功能,每個功能點講解都會從具體場景聊起,幫助大家在實戰場景下加深體會實現進階。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章