排查Java記憶體洩露實戰(內附各種排查工具介紹)

testingbang 發表於 2021-11-24
Java

今天剛剛才加一個故障review會議, 故障非常典型, google下也可以找到相似案例介紹。 在排查問題的過程中,使用了大量的工具, 發現有問題的地方還不只一個,總結一下. (本篇文章不會重點描述案例本身,重點會介紹個人對java記憶體洩露問題的排查思路和各種工具的使用)。

java記憶體洩露典型特徵


現象一: 堆/Perm 區不斷增長, 沒有下降趨勢(回收速度趕不上增長速度), 最後不斷觸發FullGC, 甚至crash(如下兩張圖是同一個應用的GC和Perm資料, GC觸發原因確認是Perm不足). 一般是現象二的晚期表現。

排查Java記憶體洩露實戰(內附各種排查工具介紹)

現象二: 每次FullGC後, 堆/Perm 區在慢慢的增長, 最後不斷觸發FullGC, 甚至crash(如下圖: 示意圖)

排查Java記憶體洩露實戰(內附各種排查工具介紹)

java記憶體洩露場景---PermGen space


原因: 說明Perm不足. Perm存放class,method相關物件,以及執行時常量物件. 如果一個應用載入了大量的class, 那麼Perm區儲存的資訊一般會比較大.另外大量的intern String物件也會導致Perm區不斷增長。此區域大小由-XX:MaxPermSize引數進行設定. (jdk8相關引數已經改變, 這裡不討論)


案例:  Groovy動態編譯class, xstream String.intern


本質原因: ClassLoader.defineClass和java.lang.String.intern在大量不適宜的場景被呼叫.


解決方案:


方案1(直接有效): 使用btrace相關工具輸出呼叫ClassLoader.defineClass棧資訊, 從棧資訊來追溯問題. (程式碼如下圖). 但Btrace 不能trace jvm native方法(官方release btrace 1.3.1中版本宣告可以trace native方法, 但嘗試無效。如果你清楚如何使用,麻煩告知一下,謝謝)。

排查Java記憶體洩露實戰(內附各種排查工具介紹)

用JProfiler來trace String.intern方法棧


方案2: dump heap, 看看哪些class有異常現象(數量), String被Perm區引用的物件資訊等.但這種方式不太直觀,可以從String資料看看發現可疑問題,沒有方案1直觀。(如下圖: 如果能在日常除錯推薦JProfiler)

排查Java記憶體洩露實戰(內附各種排查工具介紹)

方案3: 增加-XX:+TraceClassLoading和-XX:+TraceClassUnloading, 看看哪些class載入了,哪些class解除安裝了. 如果一些特殊的class一直被載入而沒有被解除安裝說明也是有問題的。(如下圖)

排查Java記憶體洩露實戰(內附各種排查工具介紹)

方案4:執行jmap -permgen(jstat -gcutil 可以檢視記憶體增長速度和區域)命令看看Perm區中的內容, 初步確定是否存在問題 (如下圖)

排查Java記憶體洩露實戰(內附各種排查工具介紹)

排查Java記憶體洩露實戰(內附各種排查工具介紹)

java記憶體洩露場景---Java heap space


原因: 長生命週期的物件引用了短生命週期(應該儘快GC回收掉)的物件,最後造成一個物件已經不能在堆區分配足夠空間. 注: 這種現象不能完全肯定是記憶體洩露, 比如: heap本身的設定的過小.


案例:  我個人沒有遇到過這種案例, 但模擬過這種情形的Demo: 參考我的《深入淺出JProfiler》文章, 也學習過其他同學的案例: 例如:引數過大並且頻繁超時導致記憶體洩露


解決方案:


  1. 觸發FullGC, dump live heap. 標記堆中物件數量, 重點關注可疑物件
  2. 觸發FullGC, dump live heap. 標記堆中物件數量, 重點關注可疑物件
  3. 對比步驟1和步驟2 相同物件的數量和大小, 找出可疑物件一一進行排查確認。
  4. 如果步驟3依然無法明確有問題的物件, 那就多執行幾次步驟1和步驟2。在此過程中可以調整GC觸發時間, 模擬真實的故障場景 :)
  5. 看看GC後堆的大小是否增長, 如果沒有不斷增長, 並且持續一段較長時間, 那基本正常。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69942496/viewspace-2843789/,如需轉載,請註明出處,否則將追究法律責任。