34-內(nèi)存泄露MAT工具看本文就夠了

一. 簡(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

image

目前絕大多數(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)獲取

    1. 在虛擬機(jī)中添加參數(shù)如下,然后在Ctrl+Break組合鍵即可獲取一份Heap Dump:

      -XX:+HeapDumpOnCtrlBreak
      
  1. 通過(guò) jmap 工具生成,在命令行中輸入:

    jmap -dump:format=b file=<文件名XX.hprof> <pid>
    
  2. Sun JConsole:?jiǎn)?dòng) jconsole.exe 并在 HotSpotDiagnostic MBean 上調(diào)用操作 dumpHeap()

  3. 使用Memory Analyzer Tools的File -> Acquire Heap Dump功能

image

圖形化界面操作,直接選擇想要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)有什么特殊情況下,選它就可以了:

image

內(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)還有很多功能可以輔助分析

image

上圖中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ō)明:

image

通過(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”

image

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

image

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

image

代碼問(wèn)題就出現(xiàn)在了第10行,list添加元素這兒。

七. Histogram

剛才我們是在Leak Suspects中進(jìn)行分析的,另外我們還可以打開(kāi)一個(gè)非常常用的工具:Histogram

image

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

image

這里每一列的參數(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í)例:

image
image

大家可以發(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)和我做朋友吧~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容