淺嘗Eclipse Memory Analyzer(MAT)

前言

在平時開發、測試過程當中、甚至是生產環境中,有時會遇到OutOfMemoryError,Java堆溢出了,這代表程序有嚴重的問題。咱們須要找形成OutOfMemoryError緣由。通常有兩種狀況:php

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


生成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,內存仍是蠻大的。linux


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




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

Histogram以下圖:eclipse

Objects:類的對象的數量。jvm

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

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

咱們發現ThreadLocal和bingo.persister.dao.Daos類的對象佔用了不少空間。spa




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應用程序,找到內存泄露的緣由,並解決。

相關文章
相關標籤/搜索