記一次oom問題排查

程式設計師大彬 發表於 2022-01-23

大家好,我是大彬~

今天給大家分享最近出現的OOM問題。

上週五早上,測試同學反饋測試環境的子系統服務一直超時,請求沒有響應。

收到這個問題之後,我有點納悶,最近這個系統也沒有改動程式碼邏輯,怎麼會突然報服務超時的問題。為避免影響測試進度,我趕緊登陸堡壘機檢視日誌,看看到底啥情況。

首先先看系統負載情況,使用top命令檢視。發現其中某個Java程式cpu一直持續停留在100%到200%之間。因為這個系統不涉及大量運算的邏輯,所以可以猜到要不就是死迴圈的問題,要不就是頻繁full gc導致。

記一次oom問題排查

檢視系統日誌發現,出現java.lang.OutOfMemoryError: Metaspace,很明顯,元空間記憶體溢位了。

接著檢視系統gc情況,使用以下命令檢視。pid為對應的Java程式id,通過top命令獲取。引數1000表示每隔1000ms列印一次記錄。

jstat -gc pid 1000

一看執行結果,果不其然,full gc 從應用程式啟動到取樣時已經觸發了幾百次!這也是cpu一直100%的原因。

記一次oom問題排查

其中還有另一個引數 MC(元空間分配記憶體大小),已經接近設定的最大元空間大小(配置的--XX:MaxMetaspaceSize=128m)。

這裡也簡單介紹下元空間。

後設資料是jdk8裡特有的資料結構,jdk7是叫永久代,到了jdk8永久代就廢棄了,使用元空間替代。元空間被分配在本地記憶體中(非堆上),預設不限制記憶體使用,可以使用 MaxMetaspaceSize 指定最大值。

元空間由兩大部分組成

  • Klass Metaspace,用來存klass的,klass是class檔案在jvm裡的執行時資料結構。
  • NoKlass Metaspace,專門來存klass相關的其他的內容,比如method,常量池等,這塊記憶體是由多塊記憶體組合起來的。

MC 就是Klass Metaspace以及NoKlass Metaspace兩者總共分配的記憶體大小,單位是KB。上圖中,MC已經接近元空間設定的上限值,也就是此時元空間記憶體已經不夠用了,導致一直觸發full gc。

然後就是dump記憶體進行分析,看看是什麼原因導致的元空間記憶體溢位。使用命令./jmap -dump:live,format=b,file=/xxx 匯出記憶體heap到xxx位置(hprof格式),然後使用MAT工具進行分析。

將hprof檔案匯入MAT工具,開啟記憶體洩漏分析(涉及公司內部原始碼,所以打了馬賽克):

記一次oom問題排查

看到這個之後,就大概知道是什麼問題了。因為最近公司內部在推廣一個漏洞監控工具,需要在服務端部署agent程式,這個工具會收集、監控應用程式執行時函式執行、資料傳輸,可以識別常見的安全缺陷和漏洞。而打碼的部分正是這個漏洞監控工具的應用包名,很可能是引入這個工具引起的問題!

進一步確認問題。開啟Histogram:

記一次oom問題排查

Shallow Heap 代表一個物件結構自身所佔用的記憶體大小,不包括其屬性引用物件所佔的記憶體。

Retained Heap 是一個物件被 GC 回收後,可釋放的記憶體大小,等於釋放物件的 Retained Heap 中所有物件的 Shallow Heap 的和。

在Histogram檢視中,選中其中一個類點選滑鼠右鍵會彈出一個選單,選擇Merge shortest paths to GC Roots,檢視當前物件到GC Root的路徑,可以過濾一些型別的引用。

記一次oom問題排查

結果如下:

記一次oom問題排查

佔用記憶體空間最多的就是漏洞監控工具的類,也基本可以確定問題所在了。

最後把這個漏洞監控工具去掉之後,重新部署之後,就不會出現服務超時的問題了。

以上就是本期OOM問題分析的整個過程~


碼字不易,如果覺得對你有幫助,可以點個贊鼓勵一下!

我是程式設計師大彬 ,專注Java後端硬核知識分享,歡迎大家關注~

本文由部落格一文多發平臺 OpenWrite 釋出!