JVM实战(一): dump文件的产生以及执行shell脚本

1. dump文件产生

1.1 絮絮叨叨

  • 目前,公司使用的OLAP组件实现了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 -ef ps -auxjps等知道该进程的进程号

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

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

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

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

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

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


参考博客:


2. 实战:模拟触发OOM

2.1 编写代码,模拟触发OOM

  • 模拟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文件,这里需要着重记录一下

2.2 题外话: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命令如下,能成功执行GcTest类
    java internet.gc.GcTest
    

方法二:借助java -cp选项

  • 通过-cp指定class文件路径的搜索,从而不用切换到src/main/java/目录

    -cp -classpath的缩写,用于指定目录或zip/jar文件的class文件搜索路径

  • 最终,java命令如下:

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

2.3 设置JVM参数,观察OOM

  • 本部分的内容,都是在src/main/java目录下,通过javajava -cp命令执行GcTest类的

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

  • 完整的Java命令

    java -cp  /.../src/main/java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • -XX:+HeapDumpOnOutOfMemoryError:出现OOM时,生成dump文件

  • -XX:+ExitOnOutOfMemoryError:出现OOM后,退出应用程序

  • -Xloggc:./gc.log:gc的日志信息写入当前目录的gc.log文件:

  • 执行结果如下:

    • 提示程序发生OOM,生成dump文件;这是由-XX:+HeapDumpOnOutOfMemoryError控制
    • 打印Terminating due to ...,提示程序因为OOM而终止;这是由-XX:+ExitOnOutOfMemoryError控制
  • 最终,src/main/目录下会生成对应的dump文件java_pid87871.hprof,以及gc日志gc.log

情况二:指定dump文件路径

  • 通过-XX:HeapDumpPath设置dump文件的路径

    java -Xms10M -Xmx10M -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=./ -XX:+PrintGCDetails -Xloggc:./gc.log internet.gc.GcTest
    
  • 执行结果如下

  • 最终,在当前目录,生成了如下文件

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

  • src/main/java目录下,直接通过java命令执行GcTest,在jvm参数中指定dump路径为./gcTest_dump(在当前目录生成名为gcTest_dump的dump文件)

    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后,应用程序直接退出
  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM常用监控工具有很多,其中一个重要的工具就是dump分析工具。dump文件是指Java进程的内存快照,可以用于分析Java进程的内存使用情况,了解Java进程内部的情况。 下面介绍几个常用的dump分析工具: 1. jmap jmap是JDK自带的一个命令行工具,可以生成Java进程的内存快照。使用jmap生成dump文件的命令如下: ``` jmap -dump:format=b,file=<filename> <pid> ``` 其中,format=b表示生成二进制格式的dump文件,file=<filename>表示指定保存dump文件的路径和文件名,<pid>表示Java进程的进程ID。 2. jstack jstack也是JDK自带的一个命令行工具,可以打印Java进程的线程堆栈信息。使用jstack生成dump文件的命令如下: ``` jstack -F <pid> > <filename> ``` 其中,-F表示在进程不响应时强制获取线程堆栈信息,<pid>表示Java进程的进程ID,> <filename>表示将线程堆栈信息输出到指定文件。 3. VisualVM VisualVM是一个功能强大的Java监控和分析工具,可以监控和分析本地和远程Java进程。VisualVM可以生成Java进程的各种信息,包括dump文件。使用VisualVM生成dump文件的步骤如下: - 在VisualVM中打开需要生成dump文件Java进程。 - 选择“Heap Dump”选项卡,点击“Heap Dump”按钮。 - 选择保存dump文件的路径和文件名,点击“Save”按钮。 4. Eclipse Memory Analyzer Eclipse Memory Analyzer是一款功能强大的Java内存分析工具,可以帮助开发人员分析Java进程的内存使用情况。Eclipse Memory Analyzer可以打开各种格式的dump文件,包括使用jmap、jstack和VisualVM生成的dump文件。 以上是常用的dump分析工具,可以帮助开发人员了解Java进程的内存使用情况。同时,需要注意的是,生成dump文件会对Java进程产生一定的影响,需要谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值