教你用MAT工具分析Java堆內(nèi)存泄漏問題的解決方法
一、MAT概述與安裝
MAT,全稱Memory Analysis Tools,是一款分析Java堆內(nèi)存的工具,可以快速定位到堆內(nèi)泄漏問題。該工具提供了兩種使用方式,一種是插件版,可以安裝到Eclipse使用,另一種是獨立版,可以直接解壓使用。
我把獨立版MAT安裝包放到了網(wǎng)盤上,方便直接下載
鏈接: https://pan.baidu.com/s/1DVHlHuSfi_4TVl2ei5YuLA
提取碼: 42qt
獨立版解壓后,其內(nèi)部文件是這樣的——
這里有一個MemoryAnalyzer.ini文件,里面有一個Xmx參數(shù),默認是-Xmx1024m,這代表MAT的最大內(nèi)存大小,根據(jù)具體分析的dump文件大小來做適當調(diào)整。
點擊MemoryAnalyzer.exe,啟動完成后,即可以使用它來檢查定位內(nèi)存泄漏相關的問題了。
二、內(nèi)存泄漏案例分析
下面,我會結(jié)合一個小案例來分享MAT的使用。
首先,用IDEA建立一個測試類——
public class example { public static void main(String[] args) { List<User> list=new ArrayList<>(); while (true){ list.add(new User()); } } } class User { private String name="demo"; public User() { } }
給這個測試類設置虛擬機參數(shù),設置如:-Xms2m -Xmx2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/local_system/git/demo/heapdump.hprof
這幾個參數(shù)的意義是:
-Xms2m -Xmx2m:堆最小內(nèi)存為2M,最大內(nèi)存為2M。這里沒有顯示設置新生代大小,它會自動分配新生代大小,分配完剩下的,就是老年代大小了。
-XX:+HeapDumpOnOutOfMemoryError:指發(fā)生內(nèi)存溢出的時候,會自動生成一個二進制的堆快照文件,這個快照文件以.hprof后綴結(jié)尾。用MAT分析堆內(nèi)存信息,就是利用這個.hprof文件。除了可以設置相應的虛擬機參數(shù)外,還可以通過jmap指令來獲取到某個進程的堆快照文件,執(zhí)行指令格式是:
jmap -dump:format=b,file=<dumpfile.hprof> <pid>
例如:jmap -dump:format=b,file=20210618.dump 7132,那么,這里20210618.dump就是自定義的dump堆轉(zhuǎn)儲文件名字,而7132是進程ID。只是使用jmap指令可能有一點不好的地方是,內(nèi)存溢出是某個時間點發(fā)生的事情,jmap指令去獲取到dump文件,存在時間差問題。而HeapDumpOnOutOfMemoryError則是在發(fā)生內(nèi)存溢出時,同時生成的,故而會更準確些。
-XX:HeapDumpPath=D:/local_system/git/demo/heapdump.hprof:內(nèi)存溢出產(chǎn)生的堆快照自動存儲路徑,可以自定義指定路徑。
其實,在實際生產(chǎn)環(huán)境里,除了這些基本參數(shù)外,還有其他的JVM參數(shù),這些參數(shù)都是用來調(diào)優(yōu)的重點所在。
這里暫且以這些參數(shù)做實驗,在運行IDEA時,可以將這些參數(shù)設置在IDEA的“Run/Debug Configurations”彈出框的VM options輸入框里,如下截圖所示——
按照以上方式設置好后,就可以運行該案例代碼了,運行一會兒后,就會出現(xiàn)以下提示——
這表明,該代碼已經(jīng)發(fā)生內(nèi)存溢出了,即ArrayList存儲的對象大小已經(jīng)超過堆內(nèi)存,導致無法進行垃圾回收,也就是出現(xiàn)內(nèi)存泄漏,進而導致內(nèi)存溢出。當然,在本地是可以看到這么簡單的異常提示的,但是在線上服務器上,就沒有那么明顯的內(nèi)存溢出提示,就需要獲取到產(chǎn)生的堆快照dump文件,然后再進一步分析堆快照信息。
三、使用MAT分析堆轉(zhuǎn)儲dump文件
我們將這個heapdump.hprof文件導入到MAT里。啟動MAT,點擊File,選擇Open Heap Dump,然后選擇對應的hprof文件
在彈出框處,選擇Leak Suspects Report,這是指內(nèi)存泄漏報告——
點擊Finish后,展示Overview主頁面如下——
Overview主頁面顯示應用程序內(nèi)存使用情況的概覽,中間的餅圖按retained size來顯示最大的對象。注意一點是,在MAT中,會有兩種大小表示,一個是Retained size,還有一個是Shallow Size,那么,兩者有什么區(qū)別呢?
- Shallow Size:表示對象自身占用的內(nèi)存大小,不包括它引用的對象。
- Retained size:當前對象內(nèi)存大小+當前對象直接或間接引用的對象大小,全部的總和,簡單理解,就是當前對象被GC后,總共能釋放的內(nèi)存大小。
1.Details顯示的是dump文件的情況,表示堆大小為1.1MB,有516個class,40.2k個Object,3個類加載器等;
2.功能視圖模塊;
3.報表模塊;
我比較喜歡用Actions的Histogram視圖和Reports的Leak Suspects報表,Histogram視圖是以類為維度來顯示其實例數(shù)和每個類的使用內(nèi)存量,可以協(xié)助我們查詢哪些類對象占用較大內(nèi)存;Leak Suspects則可以協(xié)助分析內(nèi)存泄漏的原因所在。
- Histogram視圖
以Class Name為維度,分別展示各個類的對象數(shù)量,Shallow Size,Retained size。這里有一個疑惑是,Shallow Size和Retained size沒有顯示是以什么為單位的,它默認是以byte為單位的,若要顯示地讓單位展示出來,可以這樣設置,點擊Window->Preferences
選擇最后一項,點擊Apply and Close——
再重新打開Histogram視圖,就會生效了,單位就顯示出來了——
根據(jù)這個Histogram視圖,我們可以發(fā)現(xiàn),com.example.demo.User數(shù)量和占用內(nèi)存大小都比較高,同時說明了該User對象一直沒有被GC回收掉,這時,可以右擊,彈出框有以下一些菜單選項——
List objects
使用List Object可以查看對象引用關系,這里查看引用功能,包括本對象引用外部對象with outgoing references與外部對象引用本對象with incoming references。
with outgoing references
使用該功能,可以查看對象內(nèi)部都引用了哪些外部對象,例如,這里的User,其引用外部對象情況如下:
對照這個案例的代碼,可見,在創(chuàng)建這個User對象時,內(nèi)部屬性name就會指向一個字符串地址,換言之,該User對象內(nèi)部有個引用指向了一個name字符串地址。
with incoming references
使用該功能,可以查看該對象都被哪些外部所引用了——
在案例代碼當中,是以list.add(User)來不斷存儲User對象的,如截圖所示,通過MAT可確定,存在一個ArrayList集合一直引用該User對象。
在實際開發(fā)當中,一個對象可能引用了諸多其他外部對象或者被諸多外部對象所引用,若一直引用著,說明某個對象一直存在GC ROOT可達的情況,反過來就意味著,該被引用的對象一直無法被GC回收處理,那么就可能會一直存在堆內(nèi)存里,進而造成內(nèi)存泄漏的情況。
Merge Shortest Paths to GC Roots->exclude all phantom/weak/soft etc. references
排除其他引用,只觀察GC路徑上強引用的對象,所觀察到的,都是仍存活的對象。
除此之外,Histogram視圖仍有其他功能,后期在學習過程當中,不斷進行完善。
- Leak Suspects報表
Leak Suspects報表很直觀地展現(xiàn)了一個餅圖,圖中顏色深的部分表示可能存在內(nèi)存泄漏的嫌疑。每一個模塊都有對應的詳情信息。
這里拿模塊a來講解,其詳情部分有一句話很關鍵:The memory is accumulated in one instance of "java.lang.Object[]", loaded by "
這句話翻譯過來就是,內(nèi)存累積在一個“java.lang.Object[]”實例中,由“
點擊stacktrace,進入到一個頁面,可以看到日志信息——
在這里,從下往上看異常信息,可以快速定位內(nèi)存泄漏地方出現(xiàn)在哪個類方法里的哪行代碼。
我很喜歡使用這個功能,通過獲取線上堆轉(zhuǎn)儲文件,便可以通過Leak Suspects定位到內(nèi)存泄漏快速定位在哪一行代碼。
到此這篇關于教你用MAT工具分析Java堆內(nèi)存泄漏問題的解決方法的文章就介紹到這了,更多相關Java堆內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ActiveMQ結(jié)合Spring收發(fā)消息的示例代碼
這篇文章主要介紹了ActiveMQ結(jié)合Spring收發(fā)消息的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10Java中Spring MVC接收表單數(shù)據(jù)的常用方法
Spring MVC是Spring框架中的一個模塊,用于開發(fā)基于MVC(Model-View-Controller)架構的Web應用程序,它提供了一種輕量級的、靈活的方式來構建Web應用,同時提供了豐富的功能和特性,本文給大家介紹了Spring MVC接收表單數(shù)據(jù)的方法,需要的朋友可以參考下2024-05-05Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關閉詳解
這篇文章主要給大家介紹了關于Spring Boot利用@Async異步調(diào)用:ThreadPoolTaskScheduler線程池的優(yōu)雅關閉的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧2018-05-05