一次非常典型的 JVM OOM 事故
當面對 JVM OOM 時,你會緊張嗎 ? 會不會手足無措 ?
這篇文章,分享前段時間幫一位同學梳理面對 JVM OOM 事故時的解題思路。
圖片
首先從對話中,我們可以看到內(nèi)存溢出呈現(xiàn)兩種情況:
- 運行一段時間之后,CPU 飆高 ;
- 服務假死,表現(xiàn)出來日志沒有任何輸出。
我的第一反應是:非常明顯的 JVM 內(nèi)存溢出表現(xiàn) ,不過不知道是爆炸性的內(nèi)存增長,還是緩慢的內(nèi)存增長。
于是,我回復:可以每隔一段時間 觀察 top -p Pid (進程號) 看看應用的內(nèi)存占用情況。
類似的效果見下圖:
圖片
接下來,我讓他通過 jstat -gcutil pid 1000 看看 gc 的頻率 。
圖片
從圖中,新生代 E 區(qū)和老年代基本都滿了 ,我基本可以確定是海量大對象產(chǎn)生導致 JVM OOM 了。
圖片
定時任務這四個字如電光火石般在我眼前閃過,基本八九不離十了。
圖片
接下來,他發(fā)了張那段時間的監(jiān)控圖:
圖片
哇,這張圖太有畫面感了,我都能感覺到 GC 線程在四處滅火,但依然無法釋放內(nèi)存的彷徨。
最后,我有點擔心,是不是 JVM 內(nèi)存分配小了才導致 OOM 了,同學的回復是 : 12 G 。
我覺得內(nèi)存大小還可以 ,一般情況下通過 jmap -heap pid 來查看,示例圖如下:
圖片
分析到這里,基本上我得到了如下的結論:
1、要查看代碼中是否有一次性查詢海量對象的操作 ;
2、或者有什么公共的對象一直在使用,而忘記了釋放;
3、12 G 對一般的小應用來講是綽綽有余的,而且他們的應用非高并發(fā)場景,是內(nèi)網(wǎng)系統(tǒng)。
圖片
最后,我建議觀察在日志停的那個時刻到底做了哪些事情,那才是真正的案發(fā)現(xiàn)場。
那到底是什么原因導致 JVM OOM 呢 ? 和我預期的基本一模一樣:
圖片
SQL 語句類似下圖,查詢條件沒有拼接好,導致全表掃描。
圖片
我們總結下,解決 JVM 內(nèi)存溢出問題的流程:
1、分析事故現(xiàn)場(CPU、內(nèi)存、日志);
2、通過 top -p Pid (進程號)分析進程資源占用,判斷是爆炸性的內(nèi)存增長,還是緩慢的內(nèi)存增長。
3、 jstat -gcutil pid 1000 看看 gc 的頻率 ,可以分析是否有大對象產(chǎn)生以及 查看 GC 頻率。
4、 jmap -heap pid 分析真實的 JVM 內(nèi)存占用 ,確認是否真的內(nèi)存分配得太小了。
5、 事故發(fā)生當時到底做了什么,有沒有出現(xiàn)類似于內(nèi)存或者 CPU 占用呈現(xiàn)脈沖飆高樣子。
6、 若有飆高的場景,分析彼時彼刻到底有哪些操作。
7、 若是緩慢增長,則考慮使用 MAT 結合排除法分析內(nèi)存占用。
上面的流程是我解決過內(nèi)存溢出的套路,雖然很糙,但很實用,比如曾經(jīng)幫助藝龍支付團隊解決過訂單查詢內(nèi)存溢出問題、西南某航空公司用戶中心內(nèi)存溢出問題等等。
最后,我想說:一定要注意 where 1 = 1 哦 ,真的出現(xiàn)太多次啦。