記一次記憶體溢位導致的生產事故

藍魔丶發表於2020-12-20

背景

因為同事的離職,半路被迫接手的一個視覺化專案,使用ElasticSearch作為OLAP資料庫、Docker作為部署工具等,突然有一天專案現場環境出現JVM記憶體溢位問題,被迫披掛上陣定位問題的原因

分析過程

top命令檢視系統資源佔有情況,cpu佔用不高,記憶體佔用高,並且虛擬記憶體高達16g

https://i.iter01.com/images/585001ee844fa67e7ca0ad5e99af382f410ef6f326f6245a6e6447cc401fc1c0.png

Tips:Java 程式由於自己維護堆的使用,導致呼叫 glibc 去管理記憶體的次數較少。更糟的是 Java 8 開始使用 metaspace 原空間取代永久代,而元空間是存放在作業系統本地記憶體中,那執行緒一多,每個執行緒都要使用一點元空間,每個執行緒都分配一個arena,每個都64MB,就會導致巨大的虛擬地址被分配。

free -h 檢視記憶體佔有,buffer/cache佔有很大,總記憶體剩餘7g

https://i.iter01.com/images/1a164d2ef2254636c7124ad400982c5f5f2ee58600f8f5a83075b971a2034f1a.png

Tips:buffers是用來緩衝塊裝置做的,它只記錄檔案系統的後設資料(metadata)以及 tracking in-flight pages,而cached是用來給檔案做緩衝。更通俗一點說:buffers主要用來存放目錄裡面有什麼內容,檔案的屬性以及許可權等等。而cached直接用來記憶我們開啟過的檔案和程式。

通過系統日誌中的java.lang.OutOfMemoryError與系統資源佔用情況基本定位是jvm記憶體溢位造成的

https://i.iter01.com/images/37fbed352626a1b8f626705745183c03e4508f90b4fb9ad363951e532c533da1.png

進一步使用jdk自帶效能監控工具查詢原因

  • jmap -heap 1 //檢視堆記憶體情況,無此命令引數,因為該服務使用的是docker映象的openjdk導致缺失部分jdk完整工具支援

    https://i.iter01.com/images/eacb69a3ec11e0bcbb50666836d45dc9c941f2171c2626b9cff54c3b9e688044.png

  • jstat -gc 1 250 10 //檢視垃圾回收gc狀態情況,新生代與老年代記憶體基本耗盡,FullGC高達1200多次,但是未釋放成功

    https://i.iter01.com/images/906489e18075ab35084243f6173c2a939533d51be094e202a4b6fe4913399337.png

  • jmap -histo:live 1  | head -n 100 //檢視top前100的例項數量情況,再次受阻,報錯Unable to get pid

    https://i.iter01.com/images/3021194357dd690a1753c3bc05e451cb66b4dd95ff81ae4382bef2ec282b90c9.png

執行jmap命令遇到的Unable to get pid可以採用的解決方案:

  • 在docker run時加上 --init 引數
  • 安裝Tini,使用tini作為入口程式,配置啟動java程式

接下來,我們使用Alibaba開源的診斷工具Arthas視覺化工具排查問題(PS:如果大家對Arthas不瞭解,可以檢視官方文件)

下載arthas全量jar>拷貝到容器內>成功啟動jar

docker cp 解壓絕對路徑 platform:/tmp/

docker exec -it  platform /bin/sh -c "cd /tmp/arthas; java -jar arthas-boot.jar"

https://i.iter01.com/images/3fccfe1242c8fdd9b5536e3db87465a7e487373de6a107ec7e02f082e73fe820.png

執行dashboard檢視資源佔有情況

https://i.iter01.com/images/74ded2a464b7e95b227b5d6bb4a43c117913643b08998216c6d3044905aabc62.png

通過dashboard瞭解到資源已基本耗盡,執行緒執行基本正常,最終需要通過匯出dump檔案分析記憶體分佈進行分析

執行heapdump /tmp/dump.hprof匯出dump檔案,下載dump檔案到本地,通過MAT工具進行分析

https://i.iter01.com/images/97cedb0cff9d593b90e0e8e016cfb2b369cdfdf924b53875a7e97ee6554e2463.png

最終定位問題是因為專案中未正確使用快取導致的

https://i.iter01.com/images/5bbff88b4f3d90435a3ff490c5bf08004d595bba171772a9cbc25308ea07d716.png

參考

mac下安裝MAT進行分析

連結:https://www.jianshu.com/p/88469f8f9a70

openjdk-alpine容器中的jvm如何執行dump

連結:http://www.crazy1984.com/2018/12/dev/20181227_docker_java_dump/

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章