天天看點

Java服務,記憶體OOM問題如何快速定位?

最近有朋友在知識星球提問:

沈老師,有一個Java服務出現了OOM(Out Of Memory)問題,定位了好久不得其法,請問有什麼好的思路麼?

OOM的問題,印象中之前寫過,這裡再總結一些相對通用的方案,希望能幫助到Java技術棧的同學。

某Java服務(假設PID=10765)出現了OOM,最常見的原因為:

  • 有可能是記憶體配置設定确實過小,而正常業務使用了大量記憶體
  • 某一個對象被頻繁申請,卻沒有釋放,記憶體不斷洩漏,導緻記憶體耗盡
  • 某一個資源被頻繁申請,系統資源耗盡,例如:不斷建立線程,不斷發起網絡連接配接

畫外音:無非“本身資源不夠”“申請資源太多”“資源耗盡”幾個原因。

 更具體的,可以使用以下工具逐一排查。

一、确認是不是記憶體本身就配置設定過小方法:jmap -heap 10765

Java服務,記憶體OOM問題如何快速定位?

如上圖,可以檢視新生代,老生代堆記憶體的配置設定大小以及使用情況,看是否本身配置設定過小。 二、找到最耗記憶體的對象方法:jmap -histo:live 10765 | more

Java服務,記憶體OOM問題如何快速定位?

如上圖,輸入指令後,會以表格的形式顯示存活對象的資訊,并按照所占記憶體大小排序:

  • 執行個體數
  • 所占記憶體大小
  • 類名

是不是很直覺?對于執行個體數較多,占用記憶體大小較多的執行個體/類,相關的代碼就要針對性review了。 上圖中占記憶體最多的對象是RingBufferLogEvent,共占用記憶體18M,屬于正常使用範圍。

如果發現某類對象占用記憶體很大(例如幾個G),很可能是類對象建立太多,且一直未釋放。例如:

  • 申請完資源後,未調用close()或dispose()釋放資源
  • 消費者消費速度慢(或停止消費了),而生産者不斷往隊列中投遞任務,導緻隊列中任務累積過多

畫外音:線上執行該指令會強制執行一次fgc。另外還可以dump記憶體進行分析。

三、确認是否是資源耗盡工具:

  • pstree
  • netstat

檢視程序建立的線程數,以及網絡連接配接數,如果資源耗盡,也可能出現OOM。 這裡介紹另一種方法,通過

  • /proc/${PID}/fd
  • /proc/${PID}/task

可以分别檢視句柄詳情和線程數。 例如,某一台線上伺服器的sshd程序PID是9339,檢視

  • ll /proc/9339/fd
  • ll /proc/9339/task
Java服務,記憶體OOM問題如何快速定位?

如上圖,sshd共占用了四個句柄

  • 0 -> 标準輸入
  • 1 -> 标準輸出
  • 2 -> 标準錯誤輸出
  • 3 -> socket(容易想到是監聽端口)

 sshd隻有一個主線程PID為9339,并沒有多線程。

是以,隻要

  • ll /proc/${PID}/fd | wc -l
  • ll /proc/${PID}/task | wc -l (效果等同pstree -p | wc -l)

就能知道程序打開的句柄數和線程數。

希望這1分鐘能幫到這位星球水友。

你遇到過奇葩的OOM問題麼?是怎麼定位的?歡迎分享。

本文轉自“架構師之路”公衆号,58沈劍提供。