mac下 內存分析工具mat安裝,使用mat分析:內存溢出、內存泄漏,結合jstat、jmap等命令的使用

分析前提:

  • mat的安裝詳情:https://blog.csdn.net/qq_34599254/article/details/82685110
                                https://www.cnblogs.com/trust-freedom/p/6744948.html
  • 安裝後,再具體使用過程中,發現我dump的文件有4個G,而mat默認的配置是1個G,因此需要修改默認配置,修改流程如下:https://blog.csdn.net/freshfishfish/article/details/81585369
  • jps命令查詢進程號
  • jmap -histo pid | head -n20     查看TOP20對象分佈,這個直接展示在屏幕上,也可打印到文件 jmap -histo:live [pid] >a.log

  • jstat -gc pid 3000:每隔三秒打印一次gc信息,運行結果中:
    O:old代已使用的佔當前容量百分比 ,

    YGC:從應用程序啓動到採樣時年輕代中gc次數         

    YGCT:從應用程序啓動到採樣時年輕代中gc所用時間(s)         

    FGC:從應用程序啓動到採樣時old代(全gc)gc次數         

    FGCT:從應用程序啓動到採樣時old代(全gc)gc所用時間(s)         

    GCT:從應用程序啓動到採樣時gc用的總時間(s)    

  • jmap -dump:format=b,file=/home/admin/logs/heap.hprof pid      dump出系統的堆棧詳情,便於用mat工具分析

  • 匿名內部類的含義

  • Spring創建的實例,默認都是單例的

 

真實的代碼實例不方便展示給大家,這裏寫一個簡單的demo,方便大家感知下使用了內部類的一些問題:

//定義一個Sping的服務,有個test方法,作用就是往list裏面add線程
@Component
public class MonitorServiceImpl implements MonitorService {

    private static List<Runnable> list = new ArrayList<>();

    @Override public void test(final GnssDeviceListInfo info) {
        list.add(new Runnable() {
            @Override public void run() {
                info.getDeviceType();
            }
        });
    }
}
//test方法就是往List隊列中加線程任務(new Runnable這種寫法就是匿名內部類)


//模擬一個不斷調用該服務方法的restful接口,方便用Postman調用:
@GetMapping("/test")
public  queryAllInServiceDevice(){
    while(true){
       Thread.sleep(1);
       monitorService.test(new GnssDeviceListInfo());
    }
}

項目啓動後調用/test,就會觸發test服務方法,運行一段時間後,發現系統非正常運行了,可以有很多指標,這裏不說了,我們直接分析:

第0步:jps命名找服務進程號

第一步:觀測GC信息

執行命名jstat -gccause 24203 3000,觀測gc,發現:

 

第二步:用jmap命令打印出前30個耗費資源最多的對象:

這個例子寫的太簡單,直接可以看出來,假設我們看不出來,需要使用mat工具來排查,需要先dump實時的堆棧信息:

第三步:獲取dump堆棧:jmap -dump:format=b,file=/Users/chao.zheng/heap.hprof 24203
第四步:根據mat分析生成的heap.hprof 文件:

分析第一個problem suspect 1

點擊這個案例,用樹形結構展示,方便看:

發現MonitorServiceImpl創建了大量的實例(其實上面的jmap -histo命令直接就可以看出來了(這裏只是爲了演示一些操作,讀者可不必在意啊),有接近100萬個對象創建出來了,這不是與大家掌握的知識:Spring默認是單例的有衝突嘛)

分析總結:

  1. Spring中的服務默認是單例的,這句話無可厚非
  2. 例子中的list.add(new Runnable()),會不斷的創建線程任務到list中,因爲寫法是new Runnable(),實際就會創建一個匿名內部類(如何判斷的呢,MonitorServiceImpl$1,看這個字符串末尾的$1就代表是內部類的標誌)
  3. 如果沒有線程及時的去處理list中的任務,就會導致list中的任務越來越多,如果還沒有限制List的容量大小,最終會導致佔用的內存越來越多,觀察gc信息也可以發現,fullgc越來越頻繁,系統幾乎一直在fullgc,導致系統幾乎Hung死

由於示範的例子不是很好,可能無法很好的去描述問題,有疑問歡迎留言~

 

或者直接點擊這個按鈕:

 

可能這個對象最多的不一定佔用內存最大,但我們知道java是引用的,可能他引用的對象超級多、大,可以進一步慢慢分析

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