背景
因?yàn)橥碌碾x職,半路被迫接手的一個(gè)可視化項(xiàng)目,使用ElasticSearch作為OLAP數(shù)據(jù)庫、Docker作為部署工具等,突然有一天項(xiàng)目現(xiàn)場(chǎng)環(huán)境出現(xiàn)JVM內(nèi)存溢出問題,被迫披掛上陣定位問題的原因
分析過程
top命令查看系統(tǒng)資源占有情況,cpu占用不高,內(nèi)存占用高,并且虛擬內(nèi)存高達(dá)16g

Tips:Java 程序由于自己維護(hù)堆的使用,導(dǎo)致調(diào)用 glibc 去管理內(nèi)存的次數(shù)較少。更糟的是 Java 8 開始使用 metaspace 原空間取代永久代,而元空間是存放在操作系統(tǒng)本地內(nèi)存中,那線程一多,每個(gè)線程都要使用一點(diǎn)元空間,每個(gè)線程都分配一個(gè)arena,每個(gè)都64MB,就會(huì)導(dǎo)致巨大的虛擬地址被分配。
free -h 查看內(nèi)存占有,buffer/cache占有很大,總內(nèi)存剩余7g

Tips:buffers是用來緩沖塊設(shè)備做的,它只記錄文件系統(tǒng)的元數(shù)據(jù)(metadata)以及 tracking in-flight pages,而cached是用來給文件做緩沖。更通俗一點(diǎn)說:buffers主要用來存放目錄里面有什么內(nèi)容,文件的屬性以及權(quán)限等等。而cached直接用來記憶我們打開過的文件和程序。
通過系統(tǒng)日志中的java.lang.OutOfMemoryError與系統(tǒng)資源占用情況基本定位是jvm內(nèi)存溢出造成的

進(jìn)一步使用jdk自帶性能監(jiān)控工具查找原因
-
jmap -heap 1 //查看堆內(nèi)存情況,
無此命令參數(shù),因?yàn)樵摲?wù)使用的是docker鏡像的openjdk導(dǎo)致缺失部分jdk完整工具支持https://upload-images.jianshu.io/upload_images/19453215-e9887e6503cae81f.png -
jstat -gc 1 250 10 //查看垃圾回收gc狀態(tài)情況,新生代與老年代內(nèi)存基本耗盡,F(xiàn)ullGC高達(dá)
1200多次,但是未釋放成功https://upload-images.jianshu.io/upload_images/19453215-40948db5cfedd0b2.png -
jmap -histo:live 1 | head -n 100 //查看top前100的實(shí)例數(shù)量情況,再次受阻,報(bào)錯(cuò)
Unable to get pidhttps://upload-images.jianshu.io/upload_images/19453215-01a3c4cbf3510a5f.png
執(zhí)行jmap命令遇到的Unable to get pid可以采用的解決方案:
- 在docker run時(shí)加上
--init參數(shù) - 安裝
Tini,使用tini作為入口進(jìn)程,配置啟動(dòng)java進(jìn)程
接下來,我們使用Alibaba開源的診斷工具Arthas可視化工具排查問題(PS:如果大家對(duì)Arthas不了解,可以查看官方文檔)
下載arthas全量jar>拷貝到容器內(nèi)>成功啟動(dòng)jar
docker cp 解壓絕對(duì)路徑 platform:/tmp/
docker exec -it platform /bin/sh -c "cd /tmp/arthas; java -jar arthas-boot.jar"

執(zhí)行dashboard查看資源占有情況

通過dashboard了解到資源已基本耗盡,線程執(zhí)行基本正常,最終需要通過導(dǎo)出dump文件分析內(nèi)存分布進(jìn)行分析
執(zhí)行heapdump /tmp/dump.hprof導(dǎo)出dump文件,下載dump文件到本地,通過MAT工具進(jìn)行分析

最終定位問題是因?yàn)轫?xiàng)目中未正確使用緩存導(dǎo)致的

參考
mac下安裝MAT進(jìn)行分析
https://blog.csdn.net/hanchao5272/article/details/93379202
openjdk-alpine容器中的jvm如何執(zhí)行dump
http://www.crazy1984.com/2018/12/dev/20181227_docker_java_dump/
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!


