使用Eclipse Memory Analyzer分析內存溢出

前言

在平時開發、測試過程中、甚至是生產環境中,有時會遇到OutOfMemoryError,Java堆溢出了,這表明程序有嚴重的問題。我們需要找造成OutOfMemoryError原因。一般有兩種情況:

1、內存泄露,對象已經死了,無法通過垃圾收集器進行自動回收,通過找出泄露的代碼位置和原因,纔好確定解決方案;
2、內存溢出,內存中的對象都還必須存活着,這說明Java堆分配空間不足,檢查堆設置大小(-Xmx與-Xms),檢查代碼是否存在對象生命週期太長、持有狀態時間過長的情況。
以上是處理Java堆問題的思路,具體是怎麼進行分析,這裏介紹的是使用Eclipse Memory Analyzer tool(MAT)工具分析的過程。


 


生成dump文件

     通過jvm參數--XX:-HeapDumpOnOutOfMemoryError可以讓JVM在出現內存溢出是Dump出當前的內存轉儲快照;
     或者,用jmap生產dump文件,win通過任務管理器查看tomcat的進程pid,linux用ps命令查看進程pid,然後用jmap命令(Java5:jmap -heap:format=b <pid>;Java6:jmap -dump:format=b,file=HeapDump.bin <pid>)。
    
     我這裏使用的是,我一生產環境項目,運行一段時間大概3周的樣子,就會報OutOfMemoryError。(ps:這個項目出現這種情況已經有好長一段時間了,我們之前的做法是定期的重啓tomcat,沒有去分析它的原因。)JDK64位主要參數:-Xmx3078M -Xms3078M -XX:PermSize=1024M -XX:MaxPermSize=1024M,內存還是蠻大的。


 


 


MAT安裝與介紹
     下載地址:http://www.eclipse.org/mat/downloads.php。
     通過MAT打開dump出來的內存文件,打開後如下圖:



 

     
     從上圖可以看到它的大部分功能。
     1. Histogram可以列出內存中的對象,對象的個數以及大小。
     2. Dominator Tree可以列出那個線程,以及線程下面的那些對象佔用的空間。
     3.Top consumers通過圖形列出最大的object。
     4.Leak Suspects通過MA自動分析泄漏的原因。

     

     Histogram如下圖:

     Objects:類的對象的數量。

     Shallow size:就是對象本身佔用內存的大小,不包含對其他對象的引用,也就是對象頭加成員變量(不是成員變量的值)的總和。

     Retained size:是該對象自己的shallow size,加上從該對象能直接或間接訪問到對象的shallow size之和。換句話說,retained size是該對象被GC之後所能回收到內存的總和。

     我們發現ThreadLocal和bingo.persister.dao.Daos類的對象佔用了很多空間。




 

 

     Dominator Tree如下圖:

     我們發現quartz的定時器的工作線程(10個)佔了很多的內存空間

 



 

 

 

     Top consumers如下圖:

     這裏顯示了內存中最大的對象有哪些,他們對應的類是哪些,類加載器classloader是哪些。

     有些時候,我們在這裏就可以看到代碼泄露的位置。



 
 


 

     Leak Suspects如下圖:

     從那個餅圖,該圖深色區域被懷疑有內存泄漏,可以發現整個heap才250M內存,深色區域就佔了34%。後面的描述,告訴我們quartz線程佔用了大量內存,並指出system class loader加載的"java.lang.ThreadLocal"實例的內存中聚集(消耗空間),並建議用關鍵字"java.lang.ThreadLocal$ThreadLocalMap$Entry[]"進行檢查。所以,MAT通過簡單的報告就說明了問題所在。



 
 


 

通過Leak Suspects的Problem Suspect 1點擊【Details »】,

如下圖如下圖所示的上下文菜單中選擇 List objects -> with outgoning references, 查看ThreadLocal都應用了些什麼對象。


 

 


 

現在看到ThreadLocal中引用的對象如下圖:

是dao對象

ps:該dao對象包含一個輕量級的ORM關係內容,所以Retained size比較大



 
 

下面繼續查看dao的gc ROOT

如下圖所示的上下文菜單中選擇 Path To GC Roots -> exclude weak references, 過濾掉弱引用,因爲在這裏弱引用不是引起問題的關鍵。


 



  


 

從下圖中,可以看到在org.quartz.simpl.SimpleThreadPool中保存了daos的引用。所以可以得出是是因爲定時器在運行的過程中持有大量的Daos對象應起了內存泄露。爲什麼會有那麼多的Daos呢,Daos不是一個無狀態的單例的、可以重用的嗎?繼續查看spring配置文件發現Daos的bean配置成scope="prototype",導致定時任務又是每次調用都生產新的Daos實例。由於是Daos是無狀態的,修改爲單例的,問題解決。


 



 
 

以上是通過MAT分析Tomcat應用程序,找到內存泄露的原因,並解決。

此文轉載自:https://tivan.iteye.com/blog/1487855

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章