最近,線上生產系統忽然頻繁的 JVM 內存報警!但本系統近期內並無上線改動!php
爲了能查清內存報警的緣由,使用 Eclipse Memory Analyzer tool(MAT)對 JVM Dump 文件進行了分析!java
1. 生成 dump 文件eclipse
用 jmap 生產 dump 文件ide
jmap -dump:format=b,file=HeapDump.bin <pid>
2. MAT 安裝與介紹this
下載地址:http://www.eclipse.org/mat/downloads.phpspa
經過 MAT 打開 dump 出來的內存文件,打開後以下圖:orm
Histogram 能夠列出內存中的對象,對象的個數以及大小。對象
Histogram 以下圖:blog
Objects:類的對象的數量。
Shallow size:就是對象自己佔用內存的大小,不包含對其餘對象的引用,也就是對象頭加成員變量(不是成員變量的值)的總和。
Retained size:是該對象本身的 shallow size,加上從該對象能直接或間接訪問到對象的 shallow size 之和。換句話說,retained size 是該對象被 GC 以後所能回收到內存的總和。ip
咱們發現 ConcurrentHashMap 類的對象佔用了不少空間。
Leak Suspects 以下圖:
從那個餅圖,該圖深色區域被懷疑有內存泄漏,能夠發現整個 heap 2G 內存,深色區域就佔了 98%。後面的描述,說明內存被一個實例佔用了大量內存,並指出 system class loader 加載的"java.util.concurrent.concurrentHashMap$Segmen[]"實例的內存中彙集(消耗空間),並建議用關鍵字"java.util.concurrent.concurrentHashMap$Segmen[]"進行檢查。因此,MAT 經過簡單的報告就說明了問題所在。
Dominator Tree 以下圖:>
咱們逐層打開 concurrentHashMap 的內存結構,發現 Key 很是多,而且最底層的 String 長度很大!
幸運的是該系統的下游也是咱們負責的系統,猜想 concurrentHashMap 應該是 RPC 調用返回回值待處理的內存存儲,正常狀況這個 String 的長度不很大。仔細查看, String 裏包含了多了不少詳細的異常描述信息,以前是沒有的。
排查下游系統代碼,發如今返回異常時,與以前異常拋出有所不一樣:
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 個人我的博客 ——————————