前言直到有一天你會碰到線上奇奇怪怪的問題,如:
這類問題并不像一個空指針、數(shù)組越界這樣明顯好查,這時就需要剛才提到的內(nèi)存模型、對象創(chuàng)建、線程等相關(guān)知識結(jié)合在一起來排查問題了。 正好這次借助之前的一次生產(chǎn)問題來聊聊如何排查和解決問題。 生產(chǎn)現(xiàn)象首先看看問題的背景吧: 我這其實是一個定時任務(wù),在固定的時間會開啟 N 個線程并發(fā)的從 Redis 中獲取數(shù)據(jù)進(jìn)行運算。 業(yè)務(wù)邏輯非常簡單,但應(yīng)用一般涉及到多線程之后再簡單的事情都要小心對待。 果不其然這次就出問題了。 現(xiàn)象:原本只需要執(zhí)行幾分鐘的任務(wù)執(zhí)行了幾個小時都沒退出。翻遍了所有的日志都沒找到異常。 于是便開始定位問題之路。 定位問題既然沒辦法直接從日志中發(fā)現(xiàn)異常,那就只能看看應(yīng)用到底在干嘛了。 最常見的工具就是 JDK 自帶的那一套。 這次我使用了 當(dāng)然在 dump 之前是需要知道我應(yīng)用的 pid 的,可以使用 當(dāng)然如果知道關(guān)鍵字的話直接使用 拿到 如果應(yīng)用簡單不復(fù)雜,線程這些也比較少其實可以直接打開查看。 但復(fù)雜的應(yīng)用導(dǎo)出來的日志文件也比較大還是建議用專業(yè)的分析工具。 我這里的日志比較少直接打開就可以了。 因為我清楚知道應(yīng)用中開啟的線程名稱,所以直接根據(jù)線程名就可以在日志中找到相關(guān)的堆棧:
其實其他幾個線程都和這里的堆棧類似,很明顯的看出都是在做 Redis 連接。 于是我登錄 Redis 查看了當(dāng)前的連接數(shù),發(fā)現(xiàn)已經(jīng)非常高了。 這樣 Redis 的響應(yīng)自然也就變慢了。 接著利用 解決辦法
既然找到了問題,那如何解決呢?
目前我們選擇的是第一個方案,效果很明顯。 本地模擬上文介紹的是線程相關(guān)問題,現(xiàn)在來分析下內(nèi)存的問題。 以這個類為例: https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/oom/heap/HeapOOM.java 1public class HeapOOM { 啟動參數(shù)如下:
為了更快的突出內(nèi)存問題將堆的最大內(nèi)存固定在 20M,同時在 JVM 出現(xiàn) OOM 的時候自動 dump 內(nèi)存到 執(zhí)行之后果不其然出現(xiàn)了異常: 同時對應(yīng)的內(nèi)存 dump 文件也生成了。 內(nèi)存分析這時就需要相應(yīng)的工具進(jìn)行分析了,最常用的自然就是 MAT 了。 我試了一個在線工具也不錯(文件大了就不適合了): http:///index.jsp 上傳剛才生成的內(nèi)存文件之后: 因為是內(nèi)存溢出,所以主要觀察下大對象: 也有相應(yīng)提示,這個很有可能就是內(nèi)存溢出的對象,點進(jìn)去之后: 看到這個堆棧其實就很明顯了: 在向 ArrayList 中不停的寫入數(shù)據(jù)時,會導(dǎo)致頻繁的擴(kuò)容也就是數(shù)組復(fù)制這些過程,最終達(dá)到 20M 的上限導(dǎo)致內(nèi)存溢出了。 更多建議上文說過,一旦使用了多線程,那就要格外小心。 以下是一些日常建議:
總結(jié)線上問題定位需要綜合技能,所以是需要一些基礎(chǔ)技能。如線程、內(nèi)存模型、Linux 等。 當(dāng)然這些問題沒有實操過都是紙上談兵;如果第一次碰到線上問題,不要慌張,反而應(yīng)該慶幸解決之后你又會習(xí)得一項技能。 |
|