一. 簡介
歡迎關(guān)注公眾號(hào)OpenCoder眉抬,來和我做朋友吧~
Eclipse Memory Analyzer是一個(gè)快速且功能豐富的Java堆分析器夜赵,可幫助您查找內(nèi)存泄漏并減少內(nèi)存消耗。使用Memory Analyzer分析具有數(shù)億個(gè)對(duì)象的高效堆轉(zhuǎn)儲(chǔ),快速計(jì)算對(duì)象的保留大小,查看誰阻止垃圾收集器收集對(duì)象侠姑,運(yùn)行報(bào)告以自動(dòng)提取泄漏嫌疑者。
通過MAT工具箩做,可以做以下幾方面的事兒:
- 找到最大的對(duì)象莽红,因?yàn)?MAT 提供了合理的累積大小(retained size)
- 探索對(duì)象圖邦邦,包括入站和出站引用
- 計(jì)算從GC Roots 到有趣對(duì)象的路徑
- 查找內(nèi)存浪費(fèi)安吁,如冗余 String 對(duì)象、空集合對(duì)象等...
二.下載地址
https://www.eclipse.org/mat/downloads.php
目前絕大多數(shù)開發(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ì)象和類信息。通常情況下捌锭,在寫入堆轉(zhuǎn)儲(chǔ)之前會(huì)觸發(fā)完整的 GC俘陷,因此它包含有關(guān)剩余對(duì)象的信息罗捎。
Memory Analyzer 能夠處理來自各種平臺(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 訪問的對(duì)象(比如:局部變量和類靜態(tài)變量)線程棧和局部變量
快照時(shí)刻線程的調(diào)用堆棧,以及有關(guān)本地對(duì)象的每幀信息
四.怎樣獲取Dump
Dump文件的格式為:HPROF霞掺,內(nèi)存分析器可以處理HPROF 二進(jìn)制格式的堆轉(zhuǎn)儲(chǔ)
那么我們可以從以下幾方面來獲取Dump文件:
-
Non-interactive 被動(dòng)獲纫曷贰:
通過OOM獲取,即在OutOfMemoryError后獲取一份HPROF二進(jìn)制Heap Dump文件菩彬,可以在jvm里添加參數(shù):(除非真正發(fā)生 OOM缠劝,否則不涉及任何開銷)
-XX:+HeapDumpOnOutOfMemoryError
這個(gè)參數(shù)對(duì)于生產(chǎn)系統(tǒng)來說是必須的,因?yàn)樗ǔJ沁M(jìn)一步分析內(nèi)存泄露問題的唯一方法骗灶。
默認(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
-
-
通過 jmap 工具生成脓规,在命令行中輸入:
jmap -dump:format=b file=<文件名XX.hprof> <pid>
Sun JConsole:啟動(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文件升酣,通過MAT工具進(jìn)行打開,在選好文件后态罪,會(huì)讓你選一下報(bào)告類型噩茄,默認(rèn)是內(nèi)存泄漏探測(cè)報(bào)告,沒有什么特殊情況下复颈,選它就可以了:
內(nèi)存泄漏可疑點(diǎn)(Leak Suspects)
進(jìn)入MAT后會(huì)展示內(nèi)存泄漏可疑點(diǎn)在內(nèi)存中的分布情況绩聘,還會(huì)列出具體的類,如果是自己的代碼耗啦,基本上一眼大概就能定位到某些業(yè)務(wù)凿菩,如果還不能肯定,也沒關(guān)系帜讲,接下來還有很多功能可以輔助分析
上圖中Problem Suspect1 即代表可能出現(xiàn)內(nèi)存泄露的問題分析:
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é)获黔。說明的非常清晰,這個(gè)數(shù)組占據(jù)了大量的內(nèi)存在验。
那么這個(gè)數(shù)組里到底是什么東西呢玷氏?
在上圖中的左下角大家看到有一個(gè) Details,大家點(diǎn)進(jìn)去即可看到詳細(xì)的說明:
通過Details我們可以看到腋舌,在主線程的下方引用了一個(gè) java.util.ArrayList, 這里面是一個(gè)java.lang.Object[]數(shù)組盏触,通過這里我們既可以清楚到底是什么對(duì)象占用了過大的內(nèi)存,所以MAT分析內(nèi)存是非常方便的块饺。
六.追蹤線程執(zhí)行堆棧赞辩,找到問題代碼
一旦發(fā)現(xiàn)在某個(gè)線程執(zhí)行過程中創(chuàng)建了大量對(duì)象之后,就可以嘗試找這個(gè)線程到底執(zhí)行了哪些代碼才創(chuàng)建了這些對(duì)象刨沦,我們可以點(diǎn)擊下圖中的“See stacktrace”:
點(diǎn)擊進(jìn)去后即準(zhǔn)確找到了發(fā)生問題的代碼行數(shù):
這里我們貼出對(duì)應(yīng)的源代碼诗宣,其實(shí)非常簡單,如下:
代碼問題就出現(xiàn)在了第10行想诅,list添加元素這兒召庞。
七. Histogram
剛才我們是在Leak Suspects中進(jìn)行分析的岛心,另外我們還可以打開一個(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)上右鍵打開菜單選擇 list objects ->with incoming refs 將列出該類的實(shí)例:
大家可以發(fā)現(xiàn)通過這種方式也是能直觀找到我們的大對(duì)象到底是什么诅诱。
好了髓堪,相信通過本篇文章的講解,大家對(duì)于MAT工具的使用有了一定的理解和參考娘荡,后續(xù)遇到內(nèi)存泄露干旁、內(nèi)存卡頓、大對(duì)象數(shù)據(jù)分析都可以直接通過MAT精準(zhǔn)確定炮沐,更好的優(yōu)化我們的項(xiàng)目争群。
歡迎關(guān)注公眾號(hào)OpenCoder,來和我做朋友吧~