生產服務記憶體洩漏分析過程
最近生產遇到記憶體洩漏的問題,說一下排查過程及內心歷程。
生產報錯:java.lang.OutOfMemoryError: Java heap space
堆記憶體洩漏一般有以下情況:
1, 堆記憶體本身沒有設定或者配置引數設定不合適,若按預設啟動,預設是256m?512m?,而服務本身複雜,不夠用
2, 堆中物件死了,但是GC無法回收空間,記憶體洩漏
3, 服務有大物件,當有過大物件時,而此時堆空間不足,記憶體溢位、
還有其他情況,上面三種情況都會導致java.lang.OutOfMemoryError錯誤。
所以針對上述情況排查過程如下:剛開始只是通過簡單的jmap,jstat,jstack等命令分別查看了當時服務的堆記憶體狀況,YGC,FGC次數及耗時時間,當時堆疊的情況等,看完之後,沒有任何頭緒,所以轉換思路重新生成dump檔案,執行如下命令:
jmap -dump:format=b,file=jconsole.dump pid
生產的dump檔案用專門的工具memory analyzer分析。
分析思路如下:
匯入檔案後,介面如下:
1,Actions------>Histogram
先檢視當前 Actions 下Histogram 檢視當前每個類有多少例項如下:
圖一
通過正則匹配,檢視xxx服務類所佔空間。
圖略,分析原因不在這。
2,Reports----> LeakSuspects
Leak Suspects: includes leak suspects and asystem overview 記憶體洩漏和系統概況
點開 Leak Suspects 如圖:
看到已經自動分析好的2個最可能的記憶體洩漏的問題,
問題1:
點開Details
找到
再找到
找到並開啟所佔數值大的物件
裡面載入的是mybatis對映檔案,正常,再找佔用大記憶體的
一個是sql語句,一個是結果集
再分析sql語句,找出其中幾個
body 是SQL語句
body是見截圖
是各種組裝的sql語句及查詢條件;
繼續往下看,找到
看一下用堆記憶體比較大的物件,進去檢視
其中一個,是資料庫的某個欄位
此問題的原因就是操作資料庫,根據sql找到相關的程式碼,分析一下原因。
問題2:
類載入器載入物件佔14.78%,分析如下:
找最大的物件 class,開啟如下:
只列出25個,還有9797
2,依賴的其他服務所依賴的包也載入進來,這就是多餘的包,應該去掉多餘的類包,減輕類載入器載入的jar包
分析建議:
分析服務所依賴的jar包,去掉多餘的jar包,減少不必要的類載入。
至此,兩個最可能記憶體洩漏的點已經找到。繼續驗證剛才的問題。
3, Reports-----> Top Components
Top Components: list reports for componentsbigger than 1 percent of the total heap.
列出報告中總記憶體佔用大於1%的部分。
開啟第一個:
開啟:Possible Memory Waste 可能的記憶體垃圾
開啟Details
由於String型別底層儲存的就是字元陣列,主要排查物件就是字串,如下:
找到批量插入組裝的sql語句,
略
驗證了問題一的分析。
再看空間垃圾回收率很低,如下:翻譯一下就是垃圾回收率低於20%的記憶體塊
發現這些是儲存類的反射及基本資訊儲存的
發現ibatis的sessionfactory佔用了將近50%的堆空間,還是說明sql有問題,見上面分析出來的sql原因,再查程式碼。
再看軟引用,這也會導致記憶體洩漏,如下:
開啟Details,簡單舉例一個如下:
再結合程式碼分析,推測此類該方法發生了多個方法同時呼叫的問題,請避免此類問題,減少軟引用,縮短垃圾回收時間。
再看Histogram of Softly Referenced:
看到有3300個軟引用:
經過GC,仍然有581個存活:
檢視原因:
前10個如下:
都是一些常量引用,如果這些常量沒有必要,就別再載入了。
問題分析總結:
綜上分析,建議如下:
1, sql問題,分析dump檔案發現有大量查詢語句,大量批量插入資料庫,如xxxxx查詢,需要結合程式碼檢視是否有限制條件,或者是否一次性查出資料太多等;
如xxxxxx
等批量插入,是否涉及到插入資料量很大等等,結合程式碼分析,是否有繼續優化的空間。
2, 類包問題,仔細分析一下xxx服務,到底都有哪些jar包需要依賴,把不需要依賴的排除掉,這樣可以減少多餘類對堆的佔用,提供堆的利用空間和減少GC。