OOM JVM参数配置 —— dump文件的产生以及执行shell脚本

1. dump文件产生

1.1 絮絮叨叨

  • 目前,公司使用的查询组件实现了k8s容器化

  • 如果使用宿主机磁盘存储日志,pod升级(本人是删除后重建)后,日志数据会丢失

  • 为了保证日志数据不丢失,专门配置了一个基于ceph的rbd盘挂载到/data目录

  • 由于资源问题,每个pod分配的rbd盘大小为50GB

  • jvm的配置:OOM时,产生dump文件然后退出应用;dump文件的path为默认path

    -XX:+HeapDumpOnOutOfMemoryError
    -XX:+ExitOnOutOfMemoryError
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    
  • 默认的path刚好是/data目录,一般一个dump文件50多GB

  • 一旦产生,则立马打爆pod的rbd盘

  • 老大的需求: 调研一下,看看dump文件是否能写入宿主机。宿主机那么大的存储,干嘛不利用起来

菜鸟的想法

  1. 确认为什么每次OOM,dump文件都是写入/data目录
  2. 是否可以更改dump文件的写入目录,不再写入/data目录
  3. 宿主机磁盘,在pod文件系统中,挂载的是哪个目录?(找k8s的同事确认挂载关系;验证:在目录中写入超大文件,通过df -lh命令,查看磁盘used是否增大)
  4. 指定dump文件目录,且该目录使用宿主机磁盘,需要做哪些更新?(DockerFile、JVM配置等)
  5. …,以上是自己目前能想到的问题

1.2 dump文件目录

  • 可以通过-XX:HeapDumpPath指定dump文件的目录,甚至可以指定文件名:xxx.hprof
    -XX:HeapDumpPath=/data/dump
    # 或者指定文件名
    -XX:HeapDumpPath=/data/dump/1.hprof
    
  • 未配置-XX:HeapDumpPath时,默认目录为java进程的工作目录,文件名为java_pidxxx.hprof,例如:java_pid85492.hprof
  • 注意: 有些博客说,默认文件名为java_<pid>_<date>_<time>_heapDump.hprof,但自己试验后发现并不是

小知识:如何查看java进程的工作目录?

  • 有可能这个java进程就不是我启动的,我最多能通过ps -efps -auxjps等知道该进程的进程号

  • java进程的工作目录,对应linux文件系统中cwd,current working directory

  • 通过上述命令,找到对应的进程id,假设为36

  • 通过查看/proc/3目录下的文件,筛选出该进程的cwd

     ls -l /proc/36 | grep "cwd"
    
  • 通过查看java进程的工作目录,发现启动jvm的目录是/data/xxx

  • 因此,产生dump文件的目录为:/data/xxx

  • 这也与实际发生OOM时,通过du -lhls -lh等命令排查出的dump文件目录一致


参考博客:


2. 实战:模拟触发OOM

2.1 编写代码,模拟触发OOM

  • 通过指定jvm参数,将堆内存设置为10MB,在程序中却申请不少于10MB的buffer区,使程序发生OOM

  • 具体代码如下:

    package internet.gc;
    public class GcTest {
          
          
        private final static int MB = 1024 * 1024;
        private final static int LEN = 12;
    
        public static void main(String[] args) {
          
          
            byte[][] buffer= new byte[LEN][];
            for (int i = 0; i < LEN; i++) {
          
          
                buffer[i] = new byte[1 * MB];
            }
            System.out.println("成功初始化byte数组");
        }
    }
    
  • 通过javac命令,编译GcTest.java

  • 通过java命令,执行编译好的class文件,这里需要着重记录一下

java命令如何执行带package的class文件

  • 大家平时都习惯使用IDE工具一键运行java程序,忽然让使用java命令编译、执行java程序时,可能都会折腾大半天

  • 按照初学java时的知识,通过java GcTest命令就行了

  • 结果,出错了:错误: 找不到或无法加载主类 GcTest

  • 自己的编译目录为:src/main/java/internet/gc,生成的class文件也在这个目录

  • 方法一: 使用完全限定名修饰主类GcTest,并切换到src/main/java/目录执行命令

    完全限定名类似文件路径,会定位到 internet/gc/GcTest.class文件

    java vivo.internet.gc.GcTest
    
  • 方法二: 借助java -cp选项,指定文件路径,从而不用切换到src/main/java/目录

    java -cp /.../src/main/java vivo.internet.gc.GcTest
    
  • 感谢大佬的博客:找不到或无法加载主类怎么解决?

2.2 设置JVM参数,观察OOM

情况一:使用默认路径(java进程的工作目录)

  • src/main/目录下,通过java -cp 命令,启动java程序:
    (1)出现OOM,打印dump文件、退出程序;
    (2)gc的日志信息写入当前目录的gc.log文件

    java -cp  /.../src/main/java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果:提示OOM,显示dump文件名、文件大小等信息,显示程序由于OOM终止
    在这里插入图片描述

  • 最终,src/main/目录下会生成对应的dump文件java_pid87871.hprof,以及gc日志gc.log

情况二:指定dump目录

  • src/main/java目录下,直接通过java命令执行GcTest

    java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=./ -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果如下
    在这里插入图片描述

  • 最终,在当前目录,生成了如下文件
    在这里插入图片描述

情况三:指定dump目录及文件名(gcTest_dump)

  • src/main/java目录下,直接通过java命令执行GcTest

    java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=./gcTest_dump -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果如下
    在这里插入图片描述

  • 最终,在当前目录,生成了如下文件
    在这里插入图片描述

3. 执行shell脚本

  • 不知道大家有没有这样的需求:
    (1)程序OOM后,先生成dump文件保存现场,方便分析原因
    (2)调用一个发送告警信息的接口,将程序OOM的消息立即通知运维人员以重启服务,而非等到用户炸锅
  • 要是能在产生完dump文件后,执行一个这样的shell脚本就很完美了:通过curl命令访问发送告警信息的接口,向运维人员发送告警
  • 这时候可以考虑使用-XX:OnOutOfMemoryError这个JVM参数

3.1 执行shell命令

  • 先来个简单的:打印服务因为OOM而终止,请排查原因重复服务!

    java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:OnOutOfMemoryError="echo '服务因为OOM而终止,请排查原因重复服务!'"  -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果如下:
    (1)先在工作目录生成dump文件java_pid88150.hprof
    (2)然后执行-XX:OnOutOfMemoryError参数配合的echo命令,用于打印提示信息
    在这里插入图片描述

3.2 执行shell脚本

  • 简单的shell命令无法满足需求,我们希望通过shell脚本执行复杂操作

  • shell脚本示例如下:获取系统时间,打印OOM的时间和相关提示信息

  • 配置shell脚本,通过调用shell脚本完成特定的操作

    java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=./ -XX:OnOutOfMemoryError="./1.sh"  -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果如下
    在这里插入图片描述


参考博客:


4. jmap命令产生dump文件

  • 可能会有这样的需求:
    (1)一定要等到程序OOM才能获取到dump文件吗?要是我就想看看dump文件的结构,而不是分析OOM的原因呢?
    (2)有时候,不一定到等到OOM才需要查看dump文件。程序内存飙高,也可以看看dump文件

  • 这时候,可以使用jmap命令生成指定java进程的dump文件

    jmap -dump:format=b,file=file_name <pid>
    
  • java进程的进程号为36,则jmap命令使用示例如下

    jmap -dump:format=b,file=java_pid36.hprof 36
    

注意事项

  • 启动java进程的用户为xxx,需要通过su xxx切换到该用户才能执行jmap命令

  • 本人在未切换到xxx用户,直接使用root用户时,错误信息如下

    36: well-known file /tmp/.java_pid36 is not secure: file should be owned by the current user (which is 0) but is owned by 1001
    

参考链接:Linux下获取java应用的Dump文件

5. 总结

  • -XX:+HeapDumpOnOutOfMemoryError在OOM时,产生进程的dump文件
  • 若不通过-XX:HeapDumpPath指定dump文件的生成路径,则默认在进程的工作目录生成dump文件;还可以指定dump文件的名称
  • -Xloggc,指定gc日志文件路径(包括文件名)
  • XX:OnOutOfMemoryError,可以指定shell命令、shell脚本、其他脚本等,在OOM时执行一些额外的操作(实践证明,是先产生dump文件,再执行配置)
  • -XX:+ExitOnOutOfMemoryError,OOM后,应用程序直接退出

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/120370239
OOM
今日推荐