溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

發(fā)布時(shí)間:2021-10-20 16:48:59 來源:億速云 閱讀:159 作者:柒染 欄目:大數(shù)據(jù)

java 第三方內(nèi)存分析工具M(jìn)AT怎么用,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

tags: java,troubleshooting,monitor,mat


一句話概括:MAT是一個(gè)強(qiáng)大的內(nèi)存分析工具,可以快捷、有效地幫助我們找到內(nèi)存泄露,減少內(nèi)存消耗分析工具,下文將進(jìn)行講解。

1 引言

之前的文章有提過,內(nèi)存中堆的使用情況是應(yīng)用性能監(jiān)測的重點(diǎn),而對于堆的快照,可以dump出來進(jìn)一步分析,總的來說,一般我們對于堆dump快照有三種方式:

  • 添加啟動(dòng)參數(shù)發(fā)生OOM時(shí)自動(dòng)dump: java應(yīng)用的啟動(dòng)參數(shù)一般最好都加上-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=logs/heapdump.hprof,即在發(fā)生OOM時(shí)自動(dòng)dump堆快照,但這種方式相當(dāng)來說是滯后的(需要等到發(fā)生OOM后)。

  • 使用命令按需手動(dòng)dump: 我們也可以使用jmap -dump:format=b,file=HeapDump.hprof <pid>工具手動(dòng)進(jìn)行堆dump和線程dump

  • 使用工具手動(dòng)dump: 如上一篇文章對jconsolejvisualvm的講解中講到,jvisualvm有提供dump堆快照的功能,點(diǎn)擊一下即可。

那么,對于dump出來的文件,如何對它們進(jìn)行分析?jvisualvm可以直接裝入快照文件進(jìn)行分析,而本文所介紹的MAT,相對來說內(nèi)存分析功能更強(qiáng)大,它可以自動(dòng)檢測有可能發(fā)生問題(特別是內(nèi)存溢出、內(nèi)存泄露)的地方,也可以詳細(xì)查看類內(nèi)存占用情況,方法級的調(diào)用情況等,是一個(gè)分析內(nèi)存狀況的不可多得的工具。

2 MAT工具介紹

MAT(Memory Analyzer tool)是一款內(nèi)存分析器工具,它是一個(gè)快速且功能豐富的堆內(nèi)存分析器,幫助我們查找內(nèi)存泄漏和分析高內(nèi)存消耗問題。官方網(wǎng)站是:https://www.eclipse.org/mat/,有興趣可以上去看一下。使用MAT,可以輕松實(shí)現(xiàn)以下功能:

  • 找到最大的對象,因?yàn)镸AT提供顯示合理的累積大?。?code>retained size)

  • 探索對象圖,包括inboundoutbound引用,即引用此對象的和此對象引出的。

  • 查找無法回收的對象,可以計(jì)算從垃圾收集器根到相關(guān)對象的路徑

  • 找到內(nèi)存浪費(fèi),比如冗余的String對象,空集合對象等等

3 MAT工具安裝

MAT安裝有兩種方式,一種是以eclipse插件方式安裝,一種是獨(dú)立安裝。在MAT的官方文檔中有相應(yīng)的安裝文件下載,下載地址為:https://www.eclipse.org/mat/downloads.php

  • 若使用eclipse插件安裝,help -> install new soft點(diǎn)擊ADD,在彈出框中添加插件地址:http://download.eclipse.org/mat/1.9.0/update-site/,也可以直接在下載頁面下載離線插件包,以離線方式安裝。

  • 獨(dú)立安裝,windows下,根據(jù)系統(tǒng)直接下載Windows (x86)Windows (x86_64),下載時(shí)可以選擇適合自己的鏡像,雙擊安裝即可。

4 MAT工具使用

MAT定位是內(nèi)存分析工具,它的主要功能就是對內(nèi)存快照進(jìn)行分析,幫助我們找到有可能內(nèi)存溢出或內(nèi)存泄漏的地方,因此,找到占用內(nèi)存大的對象和找出無法回收的對象是其主要目的。MAT官方文檔,地址如下:https://help.eclipse.org/2019-06/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html,對MAT的使用進(jìn)行描述,有興趣的同學(xué)可以上去看看。下面主要對MAT的常用概念、常用的功能進(jìn)行介紹。

下文中,以java-monitor-example(https://github.com/mianshenglee/my-example/tree/master/java-monitor-example)為例,此示例是一個(gè)簡單的spring boot工程,里面的一個(gè)controller中的user/oom接口調(diào)用的service對象通過List成員不斷地添加User對象,最終導(dǎo)致OOM的發(fā)生,應(yīng)用的啟動(dòng)參數(shù)是-Xms64m -Xmx64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${APP_HOME}/logs/heapdump.hprof。

4.1 MAT相關(guān)概念說明

4.1.1 內(nèi)存泄漏與內(nèi)存溢出

  • 內(nèi)存泄露:對象已經(jīng)沒用了(不被任何程序邏輯所需要),還存在被根元素引用的情況,無法通過垃圾收集器進(jìn)行自動(dòng)回收,需要通過找出泄漏的代碼位置和原因,才好確定解決方案;

  • 內(nèi)存溢出:內(nèi)存中的對象都還存活著,JVM的堆分配空間不足,需要檢查堆設(shè)置大?。?Xmx與-Xms),代碼是否存在對象生命周期太長、持有狀態(tài)時(shí)間過長的情況。

4.1.2 引用(強(qiáng)引用,軟引用,弱引用,虛引用)

  • Strong Ref(強(qiáng)引用):強(qiáng)可達(dá)性的引用,對象保存在內(nèi)存中,只有去掉強(qiáng)可達(dá),對象才被回收,通常我們編寫的代碼都是Strong Ref。

  • Soft Ref(軟引用):對應(yīng)軟可達(dá)性,只要有足夠的內(nèi)存,就一直保持對象,直到發(fā)現(xiàn)內(nèi)存吃緊且沒有Strong Ref時(shí)才回收對象。一般可用來實(shí)現(xiàn)緩存,通過java.lang.ref.SoftReference類實(shí)現(xiàn)。

  • Weak Ref(弱引用):比Soft Ref更弱,當(dāng)發(fā)現(xiàn)不存在Strong Ref時(shí),立刻回收對象而不必等到內(nèi)存吃緊的時(shí)候。通過java.lang.ref.WeakReference和java.util.WeakHashMap類實(shí)現(xiàn)。

  • Phantom Ref(虛引用):根本不會在內(nèi)存中保持任何對象,你只能使用Phantom Ref本身。一般用于在進(jìn)入finalize()方法后進(jìn)行特殊的清理過程,通過 java.lang.ref.PhantomReference實(shí)現(xiàn)。

4.1.3 shallow heapretained heap

  • shallow heap:對象本身占用內(nèi)存的大小,也就是對象頭加成員變量(不是成員變量的值)的總和,如一個(gè)引用占用32或64bit,一個(gè)integer占4bytes,Long占8bytes等。如簡單的一個(gè)類里面只有一個(gè)成員變量int i,那么這個(gè)類的shallo size是12字節(jié),因?yàn)閷ο箢^是8字節(jié),成員變量int是4字節(jié)。常規(guī)對象(非數(shù)組)的Shallow size有其成員變量的數(shù)量和類型決定,數(shù)組的shallow size有數(shù)組元素的類型(對象類型、基本類型)和數(shù)組長度決定。

  • retained heap:如果一個(gè)對象被釋放掉,那會因?yàn)樵搶ο蟮尼尫哦鴾p少引用進(jìn)而被釋放的所有的對象(包括被遞歸釋放的)所占用的heap大小,即對象X被垃圾回收器回收后能被GC從內(nèi)存中移除的所有對象之和。相對于shallow heap,Retained heap可以更精確的反映一個(gè)對象實(shí)際占用的大?。ㄈ粼搶ο筢尫?,retained heap都可以被釋放)。

4.1.4 outgoing referencesincoming references

  • outgoing references :表示該對象的出節(jié)點(diǎn)(被該對象引用的對象)。

  • incoming references :表示該對象的入節(jié)點(diǎn)(引用到該對象的對象)。

4.1.5 Dominator Tree

將對象樹轉(zhuǎn)換成Dominator Tree能幫助我們快速的發(fā)現(xiàn)占用內(nèi)存最大的塊,也能幫我們分析對象之間的依賴關(guān)系。Dominator Tree有以下幾個(gè)定義:

  • 對象X Dominator(支配)對象Y,當(dāng)且僅當(dāng)在對象樹中所有到達(dá)Y的路徑都必須經(jīng)過X

  • 對象Y的直接Dominator,是指在對象樹中距離Y最近的Dominator

  • Dominator tree利用對象樹構(gòu)建出來。在Dominator tree中每一個(gè)對象都是他的直接Dominator的子節(jié)點(diǎn)。

對象樹和Dominator tree的對應(yīng)關(guān)系如下:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

如上圖,因?yàn)锳和B都引用到C,所以A釋放時(shí),C內(nèi)存不會被釋放。所以這塊內(nèi)存不會被計(jì)算到A或者B的Retained Heap中,因此,對象樹在轉(zhuǎn)換成Dominator tree時(shí),會A、B、C三個(gè)是平級的。

4.1.6 Garbage Collection Roots(GC root)

在執(zhí)行GC時(shí),是通過對象可達(dá)性來判斷是否回收對象的,一個(gè)對象是否可達(dá),也就是看這個(gè)對象的引用連是否和GC Root相連。一個(gè)GC root指的是可以從堆外部訪問的對象,有以下原因可以使一個(gè)對象成為GC root對象。

  • System Class: 通過bootstrap/system類加載器加載的類,如rt.jar中的java.util.*

  • JNI Local: JNI方法中的變量或者方法形參

  • JNI Global:JNI方法中的全局變量

  • Thread Block:線程里面的變量,一個(gè)活著的線程里面的對象肯定不能被回收

  • Thread:處于激活狀態(tài)的線程

  • Busy Monitor:調(diào)用了wait()、notify()方法,或者是同步對象,例如調(diào)用synchronized(Object) 或者進(jìn)入一個(gè)synchronized方法后的當(dāng)前對象

  • Java Local:本地變量,例如方法的輸入?yún)?shù)或者是方法內(nèi)部創(chuàng)建的仍在線程堆棧里面的對象

  • Native Stack:Java 方法中的變量或者方法形參.

  • Finalizable:等待運(yùn)行finalizer的對象

  • Unfinalized:有finalize方法,但未進(jìn)行finalized,且不在finalizer隊(duì)列的對象。

  • Unreachable:通過其它root都不可達(dá)的對象,MAT會把它標(biāo)記為root以便于分析回收。

  • Java Stack Frame:java棧幀

  • Unknown

4.2 Leak Suspects自動(dòng)分析泄漏

當(dāng)發(fā)生OOM獲取到heapdump.hprof文件或者手動(dòng)dump出文件后,使用MAT打開文件。打開后默認(rèn)會提示是否進(jìn)行內(nèi)存泄漏檢測報(bào)告(如果打開Dump時(shí)跳過了的話,也可以從其它入口進(jìn)入工具欄上的 Run Expect System Test -> Leak Suspects),如下圖所示:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

選擇是后進(jìn)入報(bào)告內(nèi)容,此報(bào)告內(nèi)容會幫我們分析的可能有內(nèi)存泄露嫌疑的地方,它會把累積內(nèi)存占用較大的通過餅狀圖顯示出來。如下圖所示:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

如上圖,報(bào)告已指出UserService占用了76.73%的內(nèi)存,而這些內(nèi)存都在Object[]這個(gè)數(shù)組中。因此,很大一種可能是這個(gè)對象中的數(shù)組數(shù)量過大。點(diǎn)擊Details可以查看更詳細(xì)的內(nèi)容:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

在此詳細(xì)而,Shortest Paths To the Accumulation Point可以顯示出到GC roots的最短路徑,由此可以分析是由于和哪個(gè)GC root相連導(dǎo)致當(dāng)前Retained Heap占用相當(dāng)大的對象無法被回收,本示例中,GC root是線程的本地變量(java local)。Accumulated Objects in Dominator TreeDominator Tree為視圖,可以方便的看出受當(dāng)前對象“支配”的對象中哪個(gè)占用Retained Heap比較大。圖中可看出對象都在ArrayList中,而ArrayList下有Object數(shù)組,數(shù)組下是User對象。此可以知道問題出在哪里了。需要針對這個(gè)位置,查看代碼,找出導(dǎo)致這個(gè)數(shù)組存儲的User過量的原因。

注:在原始堆轉(zhuǎn)儲文件的目錄下,MAT已經(jīng)將報(bào)告的內(nèi)容壓縮打包到一個(gè)zip文件,命名為“xxx_Leak_Suspects.zip”,整個(gè)報(bào)告是一個(gè)HTML格式的文件,可以用瀏覽器直接打開查看,可以方便進(jìn)行報(bào)告分發(fā)、分享。

4.3 histogram視圖查看對象個(gè)數(shù)與大小

點(diǎn)擊Overview頁面Actions區(qū)域內(nèi)的“Histogram視圖”或點(diǎn)擊工具欄的“histogram”按鈕,可以顯示直方圖列表,它以Class類的維度展示每個(gè)Class類的實(shí)例存在的個(gè)數(shù)、 占用的Shallow heapRetained內(nèi)存大小,可以分別排序顯示。如下圖所示:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

Shallow HeapRetained Heap的概念前面已經(jīng)講過。 對于java的對象,其成員基本都是引用。真正的內(nèi)存都在堆上,看起來是一堆原生的byte[], char[], int[],因此如果只看對象本身的內(nèi)存,數(shù)量都很小,而多數(shù)情況下,在Histogram視圖看到實(shí)例對象數(shù)量比較多的類都是一些基礎(chǔ)類型(通常都排在前面),如char[]、String、byte[],所以僅從這些是無法判斷出具體導(dǎo)致內(nèi)存泄露的類或者方法的。從上圖中,可以直接看到User對象的數(shù)量很多,有時(shí)不容易看出來的,可以使用工具欄中的group result by 可以以super class,class loader等排序,需要特別注意自定義的classLoader,如下圖:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

通常,如果Histogram視圖展示的數(shù)量多的實(shí)例對象不是基礎(chǔ)類型,而是用戶自定義的類或者有嫌疑的類,就要重點(diǎn)關(guān)注查看。想進(jìn)一步分析的,可以右鍵,選擇使用List objectsMerge Shortest Paths to GC roots等功能繼續(xù)鉆取數(shù)據(jù)。其中list objects分別有outgoing referencesincoming references,可以找出由這個(gè)對象出去的引用及通過哪些引用到這個(gè)對象的。Merge Shortest Paths to GC roots可以排除掉所有不是強(qiáng)引用的,找到這個(gè)對象的到GC root的引用路徑。最終目的就是找到占用內(nèi)存最大對象和無法回收的對象,計(jì)算從垃圾收集器根到相關(guān)對象的路徑,從而根據(jù)這個(gè)對象路徑去檢查代碼,找出問題所在。

4.4 Dominator Tree視圖

點(diǎn)擊Overview頁面Actions區(qū)域內(nèi)的“Dominator Tree視圖”或點(diǎn)擊工具欄的“open Dominator Tree”按鈕,可以進(jìn)入 Dominator Tree視圖。該視圖以實(shí)例對象的維度展示當(dāng)前堆內(nèi)存中Retained Heap占用最大的對象,以及依賴這些對象存活的對象的樹狀結(jié)構(gòu)。如下圖:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

視圖展示了實(shí)例對象名、Shallow Heap大小、Retained Heap大小、以及當(dāng)前對象的Retained Heap在整個(gè)堆中的占比。該圖是樹狀結(jié)構(gòu)的,當(dāng)上一級的對象被回收,那么,它引用的子對象都會被回收,這也是Dominator的意義,當(dāng)父節(jié)點(diǎn)的回收會導(dǎo)致子節(jié)點(diǎn)也被回收。通過此視圖,可以很方便的找出占用Retained Heap內(nèi)存最多的幾個(gè)對象,并表示出某些objects的是因?yàn)槟男﹐bjects的原因而存活。本示例中,可以看出是由于UserService中的ArrayList引用的數(shù)組存儲過量的User對象。

4.5 線程視圖查看線程棧運(yùn)行情況

點(diǎn)擊工具欄的“齒輪”按鈕,可以打開Thread Overview視圖,可以查看線程的棧幀信息,包括線程對象/線程棧信息、線程名、Shallow Heap、Retained Heap、類加載器、是否Daemon線程等信息。結(jié)合內(nèi)存Dump分析,看到線程棧幀中的本地變量,在左下方的對象屬性區(qū)域還能看到本地變量的屬性值。如按上面的示例(shortest paths to GC root),知道是由于線程Thread-12GC-root占用內(nèi)存大,在線程視圖中,就可以著重看它的屬性情況,如下圖:

java 第三方內(nèi)存分析工具M(jìn)AT怎么用

由上圖可以看到此線程調(diào)用WithOOM方法,使用了變量UserService,而變量使用了userList,它包含了大量的User對象,占用retained heap很大。

對于java應(yīng)用的內(nèi)存分析,需要對java應(yīng)用的內(nèi)存進(jìn)行dump操作,生成內(nèi)存快照后,使用MAT進(jìn)行分析,找出大對象,找到內(nèi)存泄漏或溢出的地方,從而分析代碼,解決問題。本文對MAT的使用場景,基本概念,安裝、使用進(jìn)行了詳細(xì)介紹,大家可以自己安裝一下,寫個(gè)小示例或者拿本文中的示例,實(shí)踐一下。通過本文,希望可以幫助大家更方便,更有效率地分析內(nèi)存,解決java應(yīng)用的內(nèi)存故障問題。

看完上述內(nèi)容,你們掌握java 第三方內(nèi)存分析工具M(jìn)AT怎么用的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI