如何迅速定位OOM
某Java服務(wù)(假設(shè)PID=10765)出現(xiàn)了OOM咆瘟,如何快速定位村砂?
OOM常見(jiàn)原因分析
Java服務(wù)出現(xiàn)OOM肥哎,最常見(jiàn)的原因是:
- 內(nèi)存確實(shí)分配過(guò)小,內(nèi)存確實(shí)不夠用坐榆;
- 某一個(gè)對(duì)象被頻繁申請(qǐng)拴魄,卻沒(méi)有釋放,內(nèi)存不斷泄漏席镀,導(dǎo)致內(nèi)存耗盡匹中;
- 某一個(gè)資源被頻繁申請(qǐng),系統(tǒng)資源耗盡豪诲,例如:不斷創(chuàng)建線程顶捷,不斷發(fā)起網(wǎng)絡(luò)連接;更具體的屎篱,可以按照以下步驟服赎,使用以下工具排查。
OOM定位指南
- 確認(rèn)是不是內(nèi)存本身就分配過(guò)小
方法:
jmap -heap 10765
如上圖交播,可以查看新生代重虑,老生代堆內(nèi)存的分配大小以及使用情況,看是否本身分配過(guò)小秦士。
- 找到最耗內(nèi)存的對(duì)象
方法:
jmap -histo:live 10765 | more
如上圖嚎尤,輸入命令后,會(huì)以表格的形式顯示存活對(duì)象的信息伍宦,并按照所占內(nèi)存大小排序:
- 實(shí)例數(shù);
- 所占內(nèi)存大蟹α骸次洼;
- 類名;
是不是很直觀遇骑?對(duì)于實(shí)例數(shù)較多卖毁,占用內(nèi)存大小較多的實(shí)例/類,相關(guān)的代碼就要針對(duì)性review了。
需要說(shuō)明的是亥啦,jmap -histo:live 會(huì)執(zhí)行一次FGC炭剪,如果仍無(wú)法定位,可dump內(nèi)存翔脱,通過(guò)Java內(nèi)存分析工具M(jìn)AT(Memory Analyzer Tool)*線下進(jìn)行分析奴拦。上圖中占內(nèi)存最多的對(duì)象是RingBufferLogEvent,共占用內(nèi)存18M届吁,屬于正常使用范圍错妖。如果發(fā)現(xiàn)某類對(duì)象占用內(nèi)存很大(例如幾個(gè)G),很可能是類對(duì)象創(chuàng)建太多疚沐,且一直未釋放暂氯。
例如:
(1)申請(qǐng)完資源后,未調(diào)用close()或dispose()釋放資源亮蛔;
(2)消費(fèi)者消費(fèi)速度慢(或停止消費(fèi)了)痴施,而生產(chǎn)者不斷往隊(duì)列中投遞任務(wù),導(dǎo)致隊(duì)列中任務(wù)累積過(guò)多究流;
- 確認(rèn)是否是資源耗盡工具:
1. pstree
2. netstat
查看進(jìn)程創(chuàng)建的線程數(shù)辣吃,以及網(wǎng)絡(luò)連接數(shù),如果資源耗盡梯嗽,也可能出現(xiàn)OOM齿尽。這里介紹另一種方法,通過(guò)
1. /proc/${PID}/fd
2. /proc/${PID}/task
可以分別查看句柄詳情和線程數(shù)灯节。例如循头,某一臺(tái)線上服務(wù)器的sshd進(jìn)程PID是9339,執(zhí)行:ll /proc/9339/fdll /proc/9339/task如上圖炎疆,sshd共占用了四個(gè)句柄
(1)0 -> 標(biāo)準(zhǔn)輸入卡骂;
(2)1 -> 標(biāo)準(zhǔn)輸出;
(3)2 -> 標(biāo)準(zhǔn)錯(cuò)誤輸出形入;
(4)3 -> socket(容易想到是監(jiān)聽(tīng)端口)全跨;
sshd只有一個(gè)主線程PID為9339,并沒(méi)有多線程亿遂。所以浓若,只要
ll /proc/${PID}/fd | wc -l
ll /proc/${PID}/task | wc -l (效果等同pstree -p | wc -l)
就能知道進(jìn)程打開(kāi)的句柄數(shù)和線程數(shù)。
你學(xué)會(huì)了么?要是覺(jué)得有收獲,給我點(diǎn)個(gè)贊吧~