java配置OOM时保存堆转储文件

当Java程序发生OOM(OutOfMemoryError)时,如果想要自动转储堆内存以便分析,可以在启动JVM时配置下列参数:

-XX:+HeapDumpOnOutOfMemoryError

这个参数可以让JVM在抛出OOM异常时自动生成heap dump文件。

-XX:HeapDumpPath=./java_pid.hprof

指定生成的heap dump文件的存放路径和文件名,这里使用了pid作为文件名的一部分,可以将不同时间的堆转储区分开。

-XX:OnOutOfMemoryError=<命令>

当OOM发生时,可以执行指定的命令,例如用于通知或执行脚本。

所以典型的带有堆转储的启动参数可以是:

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./java_pid.hprof -Xmx512m ...

这样就可以在OOM发生时自动获取堆转储文件。分析这个文件可以定位内存泄漏或其他内存问题。

hprof文件自定义

一开始我想要使用生成时间作为heap dump文件名的一部分,配置方法如下:

-XX:HeapDumpPath=./java_oom-%t.hprof

经过实验,果然不行,原因是 HeapDumpPath 中如果使用 %t 或者 %%t 作为时间戳占位符,在运行时实际上并不能被 JVM 解析替换。

经过询问claude,得到两个解决方案,但是我还没实验,等验证过再来更新,先记录上

1.使用时间戳+计数器的方式生成唯一堆转储文件名

主要思路是:

  1. 启动Java程序时,使用一个循环脚本wrap着它

  2. 这个循环脚本中会不停地监听Java进程的PID

  3. 当检测到OOM产生时,脚本中会做两件事:

    3.1 动态设置HeapDumpPath参数,即堆转储文件的路径,文件名使用时间戳+计数器确保唯一性

    3.2 向Java进程发送kill -3 {pid} 信号,触发堆转储

这里的关键就是一旦监听到OOM事件,动态设置一个唯一的文件名,然后通知Java进程进行堆转储。

具体示例脚本代码:

# oom_wrap.sh
COUNT=1
while true; do
   PID=$(pgrep -f java) 
   if [ $? -ne 0 ]; then
     echo "Java process died"
     exit 1
   fi
   FILE=heapdump-${DATE}+${COUNT}.hprof  
   gsignal -s 3 -p heapdumpfile $FILE $PID 
   let COUNT=COUNT+1 
   sleep 10
done

启动Java程序时,用这个脚本包着:

./oom_wrap.sh java -Xmx128m ...

这里提到的gsignal是用来向进程发送信号的工具。

每次转储后复制文件到新的唯一文件名中

可以使用 Linux 的 crontab 定时任务来完成。

例如配置一个每小时执行的脚本:/path/to/heapdump-copy.sh

# heapdump-copy.sh

DUMP_DIR=/path/to/dumps

cd $DUMP_DIR

if [ -n "$(ls -1t java_oom_*.hprof | head -n 1)" ]; then
    NEWEST_DUMP=$(ls -1t java_oom_*.hprof | head -n 1)
    NEW_FILENAME=java_oom_$(date +%Y%m%d%H%M).hprof
    
    cp $NEWEST_DUMP $NEW_FILENAME
    
    echo "Copied new heap dump file $NEW_FILENAME"
fi 

脚本逻辑:

  1. 检查 dumps 目录下最新的 java 堆转储文件
  2. 如果存在的话,复制到带时间戳的新文件名中

然后在 crontab 中增加定时任务:

0 * * * * /path/to/heapdump-copy.sh  

这样就可以每小时检查并复制最新生成的 OOM 堆转储文件了。

crontab 定时任务还可以结合日志记录、监控报警使用,让自动化堆转储复制更加可靠。

你可能感兴趣的:(java,java,开发语言)