一. 簡(jiǎn)介
歡迎關(guān)注公眾號(hào)OpenCoder,來(lái)和我做朋友吧~
Eclipse Memory Analyzer是一個(gè)快速且功能豐富的Java堆分析器,可幫助您查找內(nèi)存泄漏并減少內(nèi)存消耗。使用Memory Analyzer分析具有數(shù)億個(gè)對(duì)象的高效堆轉(zhuǎn)儲(chǔ),快速計(jì)算對(duì)象的保留大小,查看誰(shuí)阻止垃圾收集器收集對(duì)象,運(yùn)行報(bào)告以自動(dòng)提取泄漏嫌疑者。
通過(guò)MAT工具,可以做以下幾方面的事兒:
- 找到最大的對(duì)象,因?yàn)?MAT 提供了合理的累積大?。╮etained size)
- 探索對(duì)象圖,包括入站和出站引用
- 計(jì)算從GC Roots 到有趣對(duì)象的路徑
- 查找內(nèi)存浪費(fèi),如冗余 String 對(duì)象、空集合對(duì)象等...
二.下載地址
https://www.eclipse.org/mat/downloads.php

目前絕大多數(shù)開(kāi)發(fā)工具都已經(jīng)使用IDEA了,因此大家下載獨(dú)立的MAT即可,注意:獨(dú)立的MAT運(yùn)行需要在JDK11及以上的環(huán)境。
三.Heap Dump
首先了解下Heap Dump,它也叫堆轉(zhuǎn)儲(chǔ)文件,是java進(jìn)程在某個(gè)時(shí)間內(nèi)的快照。它在觸發(fā)快照的時(shí)候保存了很多信息:java對(duì)象和類信息。通常情況下,在寫(xiě)入堆轉(zhuǎn)儲(chǔ)之前會(huì)觸發(fā)完整的 GC,因此它包含有關(guān)剩余對(duì)象的信息。
Memory Analyzer 能夠處理來(lái)自各種平臺(tái)的 HPROF 二進(jìn)制堆轉(zhuǎn)儲(chǔ)、IBM 系統(tǒng)轉(zhuǎn)儲(chǔ)(舊版本需要預(yù)處理)和 IBM 便攜式堆轉(zhuǎn)儲(chǔ) (PHD)。
在Heap Dump中能得到的信息包括:(以下摘自官網(wǎng))
Typical information which can be found in heap dumps (depending on the heap dump type):
-
All Objects
Class, fields, primitive values and references
-
All Classes
Classloader, name, super class, static fields
-
Garbage Collection Roots
Objects defined to be reachable by the JVM
-
Thread Stacks and Local Variables
The call-stacks of threads at the moment of the snapshot, and per-frame information about local objects
這里給大家翻譯一下為:
可以在堆轉(zhuǎn)儲(chǔ)中找到的典型信息(取決于堆轉(zhuǎn)儲(chǔ)類型):
所有對(duì)象
類、字段、原始值和引用-
All Classes
類加載器、名稱、超類、靜態(tài)字段
垃圾收集根對(duì)象GC Roots
定義為可由 JVM 訪問(wèn)的對(duì)象(比如:局部變量和類靜態(tài)變量)線程棧和局部變量
快照時(shí)刻線程的調(diào)用堆棧,以及有關(guān)本地對(duì)象的每幀信息
四.怎樣獲取Dump
Dump文件的格式為:HPROF,內(nèi)存分析器可以處理HPROF 二進(jìn)制格式的堆轉(zhuǎn)儲(chǔ)
那么我們可以從以下幾方面來(lái)獲取Dump文件:
-
Non-interactive 被動(dòng)獲?。?/strong>
通過(guò)OOM獲取,即在OutOfMemoryError后獲取一份HPROF二進(jìn)制Heap Dump文件,可以在jvm里添加參數(shù):(除非真正發(fā)生 OOM,否則不涉及任何開(kāi)銷)
-XX:+HeapDumpOnOutOfMemoryError這個(gè)參數(shù)對(duì)于生產(chǎn)系統(tǒng)來(lái)說(shuō)是必須的,因?yàn)樗ǔJ沁M(jìn)一步分析內(nèi)存泄露問(wèn)題的唯一方法。
默認(rèn)情況下,堆轉(zhuǎn)儲(chǔ)將在 JVM 的“當(dāng)前目錄”中生成。它可以使用-XX:HeapDumpPath=顯式重定向,例如-XX:HeapDumpPath=/disk2/dumps。請(qǐng)注意,轉(zhuǎn)儲(chǔ)文件可能很大,可達(dá)千兆字節(jié),因此請(qǐng)確保目標(biāo)文件系統(tǒng)有足夠的空間。
-
Interactive 主動(dòng)獲取
-
在虛擬機(jī)中添加參數(shù)如下,然后在Ctrl+Break組合鍵即可獲取一份Heap Dump:
-XX:+HeapDumpOnCtrlBreak
-
-
通過(guò) jmap 工具生成,在命令行中輸入:
jmap -dump:format=b file=<文件名XX.hprof> <pid> Sun JConsole:?jiǎn)?dòng) jconsole.exe 并在 HotSpotDiagnostic MBean 上調(diào)用操作 dumpHeap()
使用Memory Analyzer Tools的File -> Acquire Heap Dump功能

圖形化界面操作,直接選擇想要dump的pid進(jìn)程,設(shè)置存放文件的路徑即可。
五.MAT使用
準(zhǔn)備一份dump文件,通過(guò)MAT工具進(jìn)行打開(kāi),在選好文件后,會(huì)讓你選一下報(bào)告類型,默認(rèn)是內(nèi)存泄漏探測(cè)報(bào)告,沒(méi)有什么特殊情況下,選它就可以了:

內(nèi)存泄漏可疑點(diǎn)(Leak Suspects)
進(jìn)入MAT后會(huì)展示內(nèi)存泄漏可疑點(diǎn)在內(nèi)存中的分布情況,還會(huì)列出具體的類,如果是自己的代碼,基本上一眼大概就能定位到某些業(yè)務(wù),如果還不能肯定,也沒(méi)關(guān)系,接下來(lái)還有很多功能可以輔助分析

上圖中Problem Suspect1 即代表可能出現(xiàn)內(nèi)存泄露的問(wèn)題分析:
The thread java.lang.Thread @ 0xff626d38 main keeps local variables with total size 7,293,920 (89.25%) bytes.
在線程的main中持續(xù)引用本地變量達(dá)到了 7.3M的內(nèi)存,占用堆內(nèi)存89.25%
The memory is accumulated in one instance of java.lang.Object[], loaded by <system class loader>, which occupies 7,292,936 (89.24%) bytes.
內(nèi)存累積在一個(gè)“java.lang.Object[]”實(shí)例中,由“<system class loader>”加載,占用7,292,936 (89.24%)字節(jié)。說(shuō)明的非常清晰,這個(gè)數(shù)組占據(jù)了大量的內(nèi)存。
那么這個(gè)數(shù)組里到底是什么東西呢?
在上圖中的左下角大家看到有一個(gè) Details,大家點(diǎn)進(jìn)去即可看到詳細(xì)的說(shuō)明:

通過(guò)Details我們可以看到,在主線程的下方引用了一個(gè) java.util.ArrayList, 這里面是一個(gè)java.lang.Object[]數(shù)組,通過(guò)這里我們既可以清楚到底是什么對(duì)象占用了過(guò)大的內(nèi)存,所以MAT分析內(nèi)存是非常方便的。
六.追蹤線程執(zhí)行堆棧,找到問(wèn)題代碼
一旦發(fā)現(xiàn)在某個(gè)線程執(zhí)行過(guò)程中創(chuàng)建了大量對(duì)象之后,就可以嘗試找這個(gè)線程到底執(zhí)行了哪些代碼才創(chuàng)建了這些對(duì)象,我們可以點(diǎn)擊下圖中的“See stacktrace”:

點(diǎn)擊進(jìn)去后即準(zhǔn)確找到了發(fā)生問(wèn)題的代碼行數(shù):

這里我們貼出對(duì)應(yīng)的源代碼,其實(shí)非常簡(jiǎn)單,如下:

代碼問(wèn)題就出現(xiàn)在了第10行,list添加元素這兒。
七. Histogram
剛才我們是在Leak Suspects中進(jìn)行分析的,另外我們還可以打開(kāi)一個(gè)非常常用的工具:Histogram

點(diǎn)擊進(jìn)去:

這里每一列的參數(shù)做一個(gè)解釋:
- Class Name : 類名稱,java類名
- Objects : 類的對(duì)象的數(shù)量,這個(gè)對(duì)象被創(chuàng)建了多少個(gè)
- Shallow Heap :一個(gè)對(duì)象內(nèi)存的消耗大小,不包含對(duì)其他對(duì)象的引用
- Retained Heap :是shallow Heap的總和,也就是該對(duì)象被GC之后所能回收到內(nèi)存的總和
可以看到Object[]這個(gè)數(shù)組就占據(jù)了7.4MB.
在某一項(xiàng)上右鍵打開(kāi)菜單選擇 list objects ->with incoming refs 將列出該類的實(shí)例:


大家可以發(fā)現(xiàn)通過(guò)這種方式也是能直觀找到我們的大對(duì)象到底是什么。
好了,相信通過(guò)本篇文章的講解,大家對(duì)于MAT工具的使用有了一定的理解和參考,后續(xù)遇到內(nèi)存泄露、內(nèi)存卡頓、大對(duì)象數(shù)據(jù)分析都可以直接通過(guò)MAT精準(zhǔn)確定,更好的優(yōu)化我們的項(xiàng)目。
歡迎關(guān)注公眾號(hào)OpenCoder,來(lái)和我做朋友吧~