JVM 使用mat分析Dump文件排查大對象解決系統full GC問題
摘要:介紹內存分析工具Mat查找大對象的使用方法,定位full GC根源,拉升系統吞吐量,避免內存泄漏。
引言
??線上服務器頻繁發生full GC,直接拉低系統吞吐量,甚至OOM。今天我們來一起學習一下如何利用MAT(Memory Analyzer Tool)快速的定位Java程序的大對象和內存泄漏問題。本文實驗環境為Mac下安裝獨立版的Eclipse Memory Analyzer Version 1.13.0。
??對于程序員來說碼代碼容易,保證代碼的穩定性很難。有時候寫完一個功能可能只需要一天時間,但是這個功能隱藏的bug導致的線上問題排查可能需要一周或者更長時間。因此,擁有良好的代碼結構和編碼規范是一個程序員應該長期堅持并為之奮斗的一個目標。但是,百密也難免一疏,沒有百分之百沒有問題的代碼,在產品上線前,我們需要對自己的代碼進行充分的自測,解決發現問題,保證產品的穩定性并減少對用戶的困擾。今天,就給大家簡單介紹一下如何使用MAT(Memory Analyzer Tools)進行Android內存泄漏分析,我只是拋磚引玉,希望大家能夠靈活地將這個強大的工具使用起來。
??什么是Dump文件?Dump文件也稱為內存轉儲文件或內存快照文件,是一個進程或者系統在某一個給定時間的內存快照。例如當進程崩潰或進程出現其它問題時,甚至在任何時候,我們都可以使用工具備份系統或進程的內存進行調試和分析。它包含模塊信息、線程信息、堆棧調用信息、異常信息等。
查看java服務進程pid
??若想查看指定的進程,如java進程,可以通過管道符和grep命令進行查詢,命令如下:
ps -aux | grep java
or
jps -l
JMAP導出dump 文件
??在使用mat進行內存分析前,需要準備好Heap dump文件,避免服務重啟后無法復現問題。輸出jvm的heap內容到指定文件,live子選項是可選的,假如指定live選項,那么只輸出活的對象到文件:
jmap -dump:live,format=b,file=myFullGcDump.bin
??
??大家在使用上述命令時候一定要小心,如果服務器上的JVM heap過大,會造成應用“Stop the World”。建議使用參數的形式,在啟動應用程序的時候就把參數帶上,這樣會在內存溢出的時候及時的保存線程dump文件。
??把服務器上的myFullGcDump.bin文件下載到本地之后,根據dump文件大小調整mat內存,打開mat初始化文件MemoryAnalyzer.ini,將啟動內存調整至比dump大的適當大小,例如-Xmx8120m:

??現在開啟我們的dump文件分析之路吧。
梳理mat功能
??先通過一張圖來了解強大的Eclipse 插件mat的功能菜單:

??第一個菜單i用于概覽全局信息,這是最常用的一個菜單。
mat分析大對象
??下面進入本文核心,分析前面下載的dump文件。通過下圖中的Open a Heap Dump按鈕加載需要分析的本地dump文件,歷史瀏覽版文件會在按鈕下方展示。

Getting Started
??在Getting Started Wizard頁面勾選Leak Suspects Report,然后,單擊Finish輸出首份報告:

??這時可以大概看出內存中存在的問題,此處可以初步定位到內存異常的信息,但是具體問題定位還需繼續進行分析。overview界面會以餅圖的方式顯示當前消耗內存最多的幾類對象,可以使我們對當前內存消耗有一個直觀的印象。但是,除非你的程序內存泄漏特別明顯或者你正好在生成hprof文件之前復現了程序的內存泄漏場景,你才可能通過這個界面猜到程序出問題的地方。
Dominator tree
??開始 Dump 分析時,首先應使用 Dominator tree 了解各支配樹起點對象所支配內存的大小,進而了解哪幾個root對象是 GC 無法釋放大內存的原因。
??Shallow Heap 和 Retained Heap分別表示對象自身不包含引用的大小和對象自身并包含引用的大小。默認的大小單位是 Bytes,可以在 Window - Preferences 菜單中設置單位,圖中設置的是KB。
??針對非數組類型的對象,Shallow Heap的大小就是對象與它所有的成員變量大小的總和。當然這里面還會包括一些java語言特性的數據存儲單元。針對數組類型的對象,Shallow Heap的大小是數組元素對象的大小總和。
??當個別對象支配樹的 Retained Heap 很大且存在明顯傾斜時,可以重點分析它們的對象支配關系,展開RingBuffer(老鐵的代碼可能是其他類)的子樹進一步定位到問題根因,如下圖可看出最終是業務類的 LogData 對象持有的 String字符串過大:

??把鼠標懸浮在截圖中選中行上方時,會顯示value的具體值,這是 full GC的根源。其實,剛看到RingBuffer時,我們并不確認它是被哪個業務對象引用的,故需要展開子樹,查看具體是哪個業務對象。
Histogram
??功能介紹:以直方圖的方式來顯示當前內存使用情況可能更加適合較為復雜的內存泄漏分析,它默認直接羅列每個類實例的數量和累計內存占比,包括自身內存占用量(Shallow Heap)及支配對象的內存占用量(Retained Heap)。
??它支持按對象數量、Retained Heap、Shallow Heap(默認排序)等指標排序;支持按正則過濾;支持按 package、class loader、super class、class 聚類統計。使用入口:MAT 主界面 → Histogram。
??在這里,我們同樣可以發現 LogData 對象吃掉了很大內存,當然,還有另外兩個對象:

??根據Objects進行排序后可見,業務類LogData存在131072個對象,消耗的內存也比較靠前,SdkSpan和SpanData存在同樣的問題,因此這三個類必定存在問題。
使用場景:有些情況下, Dominator tree 無法展現出熱點對象(如按 class 聚合也無明顯熱點對象,此時 Dominator tree 很難做關聯分析判斷哪類對象占比高),這時可以使用 Histogram 查看所有對象所屬類的分布,快速定位占據 Retained Heap 大頭的類。
使用技巧:Integer,String 和 Object[] 一般不直接導致內存問題。為更好的組織視圖,可以通過 class loader 或 package 分組進一步排查問題。
Leak Suspects
功能介紹:具備自動檢測內存泄漏功能,羅列可能存在內存泄漏的問題點。
使用入口:一般當存在明顯的內存泄漏時,分析完Dump文件后就會展現,也可以如下圖在 MAT 主頁找到 Leak Suspects。
使用場景:需要查看引用鏈條上占用內存較多的可疑對象。這個功能可解決一些基礎問題,但復雜的問題往往幫助有限。
舉例分析
??下圖中 Leak Suspects 視圖展現了兩個線程支配了絕大部分內存。


??下圖是點擊上圖中 Keywords 中 "Details" ,獲取實例到 GC Root 的最短路徑、dominator 路徑的細信息。

??同樣可以發現使用了RingBuffer的業務類創建了很多線程。
小結
??至此本文基于線上full GC 實戰案例講解了 MAT 工具中常用功能的使用方法和適用場景,尤其是各種功能的組合使用,對快速把脈 JVM 內存問題大有裨益。當然,這里介紹的Mat工具使用方法只是冰山一角,有興趣的老鐵們可以繼續深挖。
??那么大家對于這件事都是怎么看的呢?歡迎在文章下方留言討論,三人行必有我師焉!小編會仔仔細細地看每條評論。
Reference
Buy me a coffee. ?Get red packets.
介紹內存分析工具Mat查找大對象的使用方法,定位full GC根源,拉升系統吞吐量,避免內存泄漏。
浙公網安備 33010602011771號