一次使用 Eclipse Memory Analyzer 分析 Tomcat 內存溢出

最近,線上生產系統突然頻繁的 JVM 內存報警!但本系統近期內並沒有上線改動!

爲了能查清內存報警的原因,使用 Eclipse Memory Analyzer tool(MAT)對 JVM Dump 文件進行了分析!

1. 生成 dump 文件

用 jmap 生產 dump 文件

jmap -dump:format=b,file=HeapDump.bin <pid>

2. MAT 安裝與介紹

下載地址:http://www.eclipse.org/mat/downloads.php

1_20150209180326.jpg

通過 MAT 打開 dump 出來的內存文件,打開後如下圖:

1_20150209180332.jpg 1_20150209180338.jpg

Histogram 可以列出內存中的對象,對象的個數以及大小。

Histogram 如下圖:

Objects:類的對象的數量。
Shallow size:就是對象本身佔用內存的大小,不包含對其他對象的引用,也就是對象頭加成員變量(不是成員變量的值)的總和。
Retained size:是該對象自己的 shallow size,加上從該對象能直接或間接訪問到對象的 shallow size 之和。換句話說,retained size 是該對象被 GC 之後所能回收到內存的總和。

我們發現 ConcurrentHashMap 類的對象佔用了很多空間。

1_20150209180345.jpg

Leak Suspects 如下圖:

從那個餅圖,該圖深色區域被懷疑有內存泄漏,可以發現整個 heap 2G 內存,深色區域就佔了 98%。後面的描述,說明內存被一個實例佔用了大量內存,並指出 system class loader 加載的"java.util.concurrent.concurrentHashMap$Segmen[]"實例的內存中聚集(消耗空間),並建議用關鍵字"java.util.concurrent.concurrentHashMap$Segmen[]"進行檢查。所以,MAT 通過簡單的報告就說明了問題所在。

1_20150209180403.jpg

Dominator Tree 如下圖:>

我們逐層打開 concurrentHashMap 的內存結構,發現 Key 非常多,並且最底層的 String 長度很大!

幸運的是該系統的下游也是我們負責的系統,猜測 concurrentHashMap 應該是 RPC 調用返回回值待處理的內存存儲,正常情況這個 String 的長度不很大。仔細查看, String 裏包含了多了很多詳細的異常描述信息,之前是沒有的。

1_20150209180411.jpg

排查下游系統代碼,發現在返回異常時,與之前異常拋出有所不同:

try {
} catch(Exception e) {
    throw e;
}
 
...
 
try {
} catch(Exception e) {
    throw new Exception("xxxx", e);
}
 
// 返回僞代碼
response(e.getMessage);

就是有這些許的不同,我們看下源代碼:

public Throwable(String message, Throwable cause) {
    fillInStackTrace();
    detailMessage = message;
    this.cause = cause;
}
 
public Throwable(Throwable cause) {
    fillInStackTrace();
    detailMessage = (cause==null ? null : cause.toString());
    this.cause = cause;
}
 
public String getMessage() {
    return detailMessage;
}

當沒有 message,message = cause.toString(),所以就造成了返回大量不必要的異常信息,從而影響了上游系統!

參考:

http://tivan.iteye.com/blog/1487855


—————————— 本文同步發佈於 ZHANGSR 我的個人博客  ——————————

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