線上服務內存OOM問題定位三板斧
相信大家都有感觸,線上服務內存OOM的問題,是最難定位的問題,不過歸根結底,最常見的原因:
- 本身資源不夠
- 申請的太多
- 資源耗盡
58到家架構部,運維部,58速運技術部聯合進行了一次線上服務內存OOM問題排查實戰演練,將內存OOM問題定位三板斧分享出來,希望對大家也有幫助。
題目
某服務器上部署了Java服務一枚,出現了OutOfMemoryError,請問有可能是什么原因,問題應該如何定位?
不妨設服務進程PID為10765(沒錯,就是CPU占用高的那個倒霉的進程《線上服務CPU100%問題快速定位實戰》)。
解決思路
Java服務OOM,最常見的原因為:
- 有可能是內存分配確實過小,而正常業務使用了大量內存
- 某一個對象被頻繁申請,卻沒有釋放,內存不斷泄漏,導致內存耗盡
- 某一個資源被頻繁申請,系統資源耗盡,例如:不斷創建線程,不斷發起網絡連接
更具體的,可以使用以下的一些工具逐一排查。
一、確認是不是內存本身就分配過小
方法:
- jmap -heap 10765
如上圖,可以查看新生代,老生代堆內存的分配大小以及使用情況,看是否本身分配過小。
二、找到最耗內存的對象
方法:
- jmap -histo:live 10765 | more
圖示:
如上圖,輸入命令后,會以表格的形式顯示存活對象的信息,并按照所占內存大小排序:
- 實例數
- 所占內存大小
- 類名
是不是很直觀?對于實例數較多,占用內存大小較多的實例/類,相關的代碼就要針對性review了。
上圖中占內存最多的對象是RingBufferLogEvent,共占用內存18M,屬于正常使用范圍。
如果發現某類對象占用內存很大(例如幾個G),很可能是類對象創建太多,且一直未釋放。例如:
- 申請完資源后,未調用close()或dispose()釋放資源
- 消費者消費速度慢(或停止消費了),而生產者不斷往隊列中投遞任務,導致隊列中任務累積過多
三、確認是否是資源耗盡
工具:
- pstree
- netstat
查看進程創建的線程數,以及網絡連接數,如果資源耗盡,也可能出現OOM。
這里介紹另一種方法,通過
- /proc/${PID}/fd
- /proc/${PID}/task
可以分別查看句柄詳情和線程數。
例如,某一臺線上服務器的sshd進程PID是9339,查看
- ll /proc/9339/fd
- ll /proc/9339/task
如上圖,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)
就能知道進程打開的句柄數和線程數。
作業
對線上服務器的一臺tomcat,查看proc下的fd目錄和task目錄,特別是對于句柄fd目錄的查詢,有意想不到的驚喜喲,一定要動手試試哈。
【本文為51CTO專欄作者“58沈劍”原創稿件,轉載請聯系原作者】