记一次内存溢出导致的生产事故

背景

因为同事的离职,半路被迫接手的一个可视化项目,使用ElasticSearch作为OLAP数据库、Docker作为部署工具等,突然有一天项目现场环境出现JVM内存溢出问题,被迫披挂上阵定位问题的原因

分析过程

top命令查看系统资源占有情况,cpu占用不高,内存占用高,并且虚拟内存高达16g

https://upload-images.jianshu.io/upload_images/19453215-e57674319fd596ce.png

Tips:Java 程序由于自己维护堆的使用,导致调用 glibc 去管理内存的次数较少。更糟的是 Java 8 开始使用 metaspace 原空间取代永久代,而元空间是存放在操作系统本地内存中,那线程一多,每个线程都要使用一点元空间,每个线程都分配一个arena,每个都64MB,就会导致巨大的虚拟地址被分配。

free -h 查看内存占有,buffer/cache占有很大,总内存剩余7g

https://upload-images.jianshu.io/upload_images/19453215-8804a8d76a3e0f2e.png

Tips:buffers是用来缓冲块设备做的,它只记录文件系统的元数据(metadata)以及 tracking in-flight pages,而cached是用来给文件做缓冲。更通俗一点说:buffers主要用来存放目录里面有什么内容,文件的属性以及权限等等。而cached直接用来记忆我们打开过的文件和程序。

通过系统日志中的java.lang.OutOfMemoryError与系统资源占用情况基本定位是jvm内存溢出造成的

https://upload-images.jianshu.io/upload_images/19453215-e51b368aa85a1d24.png

进一步使用jdk自带性能监控工具查找原因

  • jmap -heap 1 //查看堆内存情况,无此命令参数,因为该服务使用的是docker镜像的openjdk导致缺失部分jdk完整工具支持

    https://upload-images.jianshu.io/upload_images/19453215-e9887e6503cae81f.png
  • jstat -gc 1 250 10 //查看垃圾回收gc状态情况,新生代与老年代内存基本耗尽,FullGC高达1200多次,但是未释放成功

    https://upload-images.jianshu.io/upload_images/19453215-40948db5cfedd0b2.png
  • jmap -histo:live 1 | head -n 100 //查看top前100的实例数量情况,再次受阻,报错Unable to get pid

    https://upload-images.jianshu.io/upload_images/19453215-01a3c4cbf3510a5f.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://upload-images.jianshu.io/upload_images/19453215-150b5fa682bae35f.png

执行dashboard查看资源占有情况

https://upload-images.jianshu.io/upload_images/19453215-85fbede3b7181b9d.png

通过dashboard了解到资源已基本耗尽,线程执行基本正常,最终需要通过导出dump文件分析内存分布进行分析

执行heapdump /tmp/dump.hprof导出dump文件,下载dump文件到本地,通过MAT工具进行分析

https://upload-images.jianshu.io/upload_images/19453215-e928fbf1d658ded8.png

最终定位问题是因为项目中未正确使用缓存导致的

https://upload-images.jianshu.io/upload_images/19453215-ed4e1efcc5eb8edc.png

参考

mac下安装MAT进行分析
https://blog.csdn.net/hanchao5272/article/details/93379202

openjdk-alpine容器中的jvm如何执行dump
http://www.crazy1984.com/2018/12/dev/20181227_docker_java_dump/

本文由博客一文多发平台 OpenWrite 发布!

你可能感兴趣的:(记一次内存溢出导致的生产事故)