MAT 入門到精通(一)
儘管JVM提供了自動記憶體管理的機制,試圖降低程式設計師的開發門檻,確實也實現了這一目標,在日常開發中,我們一般都不需要關心物件的記憶體釋放。JVM大部分都是使用trace演算法來判斷一個物件是否該被回收,那麼JVM只能回收那些從gc roots不可達的物件。
如果我們在使用某些大的物件、集合物件或者一些三方包裡的資源,忘記及時釋放資源的話,還是會造成JVM的記憶體洩漏或記憶體浪費的問題。因此,如果想成為更高階的Java開發工程師,我們需要了解常見的問題排查的辦法和工具,這個系列的文章,準備介紹一個用來做JVM堆記憶體分析的工具——MAT(Memory Aanlysis Tool)。
MAT的官網在: https://www.eclipse.org/mat/ ,可以看下它的介紹——MAT是一款高效能、具備豐富功能的Java堆記憶體分析工具,可以用來排查記憶體洩漏和記憶體浪費的問題。
MAT的安裝和設定
01
1.1
在Mac上安裝MAT
MAT 支援兩種安裝方式,一種是"單機版“的,也就是說使用者不必安裝 Eclipse IDE 環境,MAT 作為一個獨立的 Eclipse RCP 應用執行;另一種是”整合版“的,也就是說 MAT 也可以作為 Eclipse IDE 的一部分,和現有的開發平臺整合。
這裡我們考慮獨立安裝,在觀望的下載頁面,選擇mac os版本的安裝檔案下載即可。
1.2
安裝中遇到的坑
-
啟動直接報錯,系統預設的workspace是隻讀的,更換掉即可。怎麼更換呢,在檔案
/Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini
中進行修改。 -
啟動後,UI介面沒反應,參考: https://www.eclipse.org/forums/index.php/t/1090889/ ,換個包即可。這個問題我遇到過很多次。
1.3
MAT的設定
配置mat的堆記憶體大小
我的電腦是8C16G的,那理論上分析10G的堆檔案沒問題,但是MAT預設的配置沒有這麼大,需要在 /Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini
檔案中進行修改。如下圖所示,我將我的MAT自己的執行時堆記憶體配置成了6G。
配置MAT的使用
MAT的配置頁面可以從Window——>Preferences找到,如下圖所示。
MAT的一般配置有幾個選項
-
Keep unreachable objects:如果勾選這個,則在分析的時候會包含dump檔案中的不可達物件;
-
Hide the getting started wizard:隱藏分析完成後的首頁,控制是否要展示一個對話方塊,用來展示記憶體洩漏分析、消耗最多記憶體的物件排序。
-
Hide popup query help:隱藏彈出查詢幫助,除非使用者通過F1或Help按鈕查詢幫助。
-
Hide Welcome screen on launch:隱藏啟動時候的歡迎介面
-
Bytes Display:設定分析結果中記憶體大小的展示單位
可以看出,MAT不僅支援HPROF檔案的分析,還支援DTFJ檔案的分析。一般sun公司系列的JVM生成的dump檔案都是HPROF格式的,IBM的JVM生成的dump檔案時DTFJ格式的。
基本概念
02
2.1
Heap Dump
Heap Dump是Java程序在某個時刻的記憶體快照,不同JVM的實現的Heap Dump的檔案格式可能不同,進而儲存的資料也可能不同,但是一般來說。
Heap Dump中主要包含當生成快照時堆中的java物件和類的資訊,主要分為如下幾類:
-
物件資訊:類名、屬性、基礎型別和引用型別
-
類資訊:類載入器、類名稱、超類、靜態屬性
-
gc roots:JVM中的一個定義,進行垃圾收集時,要遍歷可達物件的起點節點的集合
-
執行緒棧和區域性變數:快照生成時候的執行緒呼叫棧,和每個棧上的區域性變數
Heap Dump中沒有包含物件的分配資訊,因此它不能用來分析這種問題:一個物件什麼時候被建立、一個物件時被誰建立的。
2.2
Shallow vs. Retained Heap
Shallow heap 是一個物件本身佔用的堆記憶體大小。一個物件中,每個引用佔用8或64位,Integer佔用4位元組,Long佔用8位元組等等。
Retained set ,對於某個物件X來說,它的Retained set指的是——如果X被垃圾收集器回收了,那麼這個集合中的物件都會被回收,同理,如果X沒有被垃圾收集器回收,那麼這個集合中的物件都不會被回收。
Retained heap ,物件X的Retained heap指的時候它的Retained set中的所有物件的Shallow si的和,換句話說,Retained heap指的是物件X的保留記憶體大小,即由於它的存活導致多大的記憶體也沒有被回收。
leading set ,物件X可能不止有一個,這些物件統一構成了leading set。如果leading set中的物件都不可達,那麼這個leading set對應的retained set中的物件就會被回收。一般有以下幾種情況:
-
某個類的所有例項物件,這個類物件就是leading object
-
某個類記載器載入的所有類,以及這些類的例項物件,這個類載入器物件就是leading object
-
一組物件,要達到其他物件的必經路徑上的物件,就是leading object
在下面這張圖中,A和B是gc roots中的節點(方法引數、區域性變數,或者呼叫了wait()、notify()或synchronized()的物件)等等。可以看出,E的存在,會導致G無法被回收,因此E的Retained set是E和G;C的存在,會導致E、D、F、G、H都無法被回收,因此C的Retined set是C、E、D、F、G、H;A和B的存在,會導致C、E、D、F、G、H都無法被回收,因此A和B的Retained set是A、B、C、E、D、F、G、H。
2.3
Dominator Tree
MAT根據堆上的物件引用關係構建了支配樹(Dominator Tree),通過支配樹可以很方便得識別出哪些物件佔用了大量的記憶體,並可以看到它們之間的依賴關係。
如果在物件圖中,從gc root或者x上游的一個節點開始遍歷,x是y的必經節點,那麼就可以說x支配了y( dominate )。
如果在物件圖中,x支配的所有物件中,y的距離最近,那麼就可以說x直接支配( immediate dominate )y。
支配樹是基於物件的引用關係圖建立的,在支配樹中每個節點都是它的子節點的直接支配節點。基於支配樹可以很清楚得看到物件之間的依賴關係。
現在看個例子,在下面這張圖中
-
x節點的子樹就是所有被x支配的節點集合,也正式x的retained set;
-
如果x是y的直接支配節點,那麼x的支配節點也可以支配y
-
支配樹中的邊跟物件引用圖中的引用關係並不是一一對應的。
2.4
Garbage Collection Roots
在MAT中,gc roots的概念跟研究垃圾收集演算法時候的概念稍微有點不同。gc roots中的物件,是指那些可以從堆外訪問到的物件的集合。如果一個物件符合下面這些場景中的一個,就可以被認為是gc roots中的節點:
-
System Class:由bootstrap classloader載入的類,例如rt.jar,裡面的類的包名都是
java.util.*
開頭的。 -
JNI Local:native程式碼中的區域性變數,例如使用者編寫的JNI程式碼或JVM內部程式碼。
-
JNI Global:native程式碼中的全域性變數,例如使用者編寫的JNI程式碼或JVM內部程式碼。
-
Thread Block:被當前活躍的執行緒鎖引用的物件。
-
Thread:正在存活的執行緒
-
Busy Monitor:呼叫了wait()、notify()或synchronized關鍵字修飾的程式碼——例如
synchronized(object)
或synchronized
方法。 -
Java Local:區域性變數。例如函式的輸入引數、正在執行的執行緒棧裡建立的物件。
-
Native Stack:native程式碼的輸入或輸出引數,例如使用者定義的JNI程式碼或JVM的內部程式碼。在檔案/網路IO方法或反射方法的引數。
-
Finalizable:在finalize佇列中等待它的finalizer物件執行的物件。
-
Unfinalized:過載了finalize方法,但是還沒有進入finalize佇列中的物件。
-
Unreachable:從任何gc roots節點都不可達的物件,在MAT中將這些物件視為root節點,如果不這麼做,就不能對這些物件進行分析。
-
Java Stack Frame:Java棧幀,用於存放區域性變數。只在dump檔案被解析的時候會將java stack frame視為物件。
-
Unknown:沒有root型別的物件。有些dump檔案(例如IBM的Portable Heap Dump)沒有root資訊。
獲取Dump檔案
03
-
通過MAT生成dump檔案 通過這個路徑找到生成dump檔案的對話方塊 選擇一個程序,點選finish即可
-
通過jmap命令生成dump檔案
-
命令格式:
jmap -dump:live,format=b,file=heap.bin <pid>
-
注意:如果要保留heapdump中的不可達物件,則需要把”:live“去掉,即使用命令”jmap -dump,format=b,file=heap.bin <pid> “
-
通過設定JVM引數自動生成 使用
-XX:+HeapDumpOnOutOfMemoryError
這個JVM引數,在Java程序執行過程中發生OOM的時候就會生成一個heapdump檔案,並寫入到指定目錄,一般用-XX:HeapDumpPath=${HOME}/logs/test
來設定。
本號專注於後端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這裡有所收穫。