大家好,我是大彬~
今天給大家分享最近出現的OOM問題。
上週五早上,測試同學反饋測試環境的子系統服務一直超時,請求沒有響應。
收到這個問題之後,我有點納悶,最近這個系統也沒有改動程式碼邏輯,怎麼會突然報服務超時的問題。為避免影響測試進度,我趕緊登陸堡壘機檢視日誌,看看到底啥情況。
首先先看系統負載情況,使用top
命令檢視。發現其中某個Java程式cpu一直持續停留在100%到200%之間。因為這個系統不涉及大量運算的邏輯,所以可以猜到要不就是死迴圈的問題,要不就是頻繁full gc導致。
檢視系統日誌發現,出現java.lang.OutOfMemoryError: Metaspace
,很明顯,元空間記憶體溢位了。
接著檢視系統gc情況,使用以下命令檢視。pid為對應的Java程式id,通過top命令獲取。引數1000
表示每隔1000ms列印一次記錄。
jstat -gc pid 1000
一看執行結果,果不其然,full gc 從應用程式啟動到取樣時已經觸發了幾百次!這也是cpu一直100%的原因。
其中還有另一個引數 MC(元空間分配記憶體大小),已經接近設定的最大元空間大小(配置的--XX:MaxMetaspaceSize=128m)。
這裡也簡單介紹下元空間。
後設資料是jdk8裡特有的資料結構,jdk7是叫永久代,到了jdk8永久代就廢棄了,使用元空間替代。元空間被分配在本地記憶體中(非堆上),預設不限制記憶體使用,可以使用 MaxMetaspaceSize 指定最大值。
元空間由兩大部分組成
Klass Metaspace
,用來存klass的,klass是class檔案在jvm裡的執行時資料結構。NoKlass Metaspac
e,專門來存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工具,開啟記憶體洩漏分析(涉及公司內部原始碼,所以打了馬賽克):
看到這個之後,就大概知道是什麼問題了。因為最近公司內部在推廣一個漏洞監控工具,需要在服務端部署agent程式,這個工具會收集、監控應用程式執行時函式執行、資料傳輸,可以識別常見的安全缺陷和漏洞。而打碼的部分正是這個漏洞監控工具的應用包名,很可能是引入這個工具引起的問題!
進一步確認問題。開啟Histogram:
Shallow Heap 代表一個物件結構自身所佔用的記憶體大小,不包括其屬性引用物件所佔的記憶體。
Retained Heap 是一個物件被 GC 回收後,可釋放的記憶體大小,等於釋放物件的 Retained Heap 中所有物件的 Shallow Heap 的和。
在Histogram檢視中,選中其中一個類點選滑鼠右鍵會彈出一個選單,選擇Merge shortest paths to GC Roots,檢視當前物件到GC Root的路徑,可以過濾一些型別的引用。
結果如下:
佔用記憶體空間最多的就是漏洞監控工具的類,也基本可以確定問題所在了。
最後把這個漏洞監控工具去掉之後,重新部署之後,就不會出現服務超時的問題了。
以上就是本期OOM問題分析的整個過程~
碼字不易,如果覺得對你有幫助,可以點個贊鼓勵一下!
我是程式設計師大彬 ,專注Java後端硬核知識分享,歡迎大家關注~
本文由部落格一文多發平臺 OpenWrite 釋出!