Java 应用产生 core dump

什么是core dump

在 Linux 系统中,当一个进程崩溃(例如,由于段错误或其他严重错误),它通常会产生一个称为 "核心转储"(core dump)的文件。核心转储文件包含了进程崩溃时的内存映像和一些执行上下文信息,如寄存器状态、程序计数器位置、内存管理信息等。这些信息对于开发者来说非常有价值,因为它们可以用来调试程序,了解程序崩溃的原因。

Java 进程如何产生 core dump

对于正在运行的 Java 进程, 它就是一个标准的linux 进程, 可以使用 linux 上的各种工具来产生 core dump, 比如 gcore 或者 kill :

$ gcore <pid>
$ kill -11 <pid>

对于 Java 应用程序自身, 当它奔溃的时候, 默认会产生 core dump, 因为它有如下默认参数-XX:+CreateCoredumpOnCrash:

$ java -XX:+PrintFlagsFinal -version | grep Core
     bool CreateCoredumpOnCrash                    = true                                      {product} {default}

虽然上面的方式都能产生 core dump, 但是很有可能你并不能看到 core dump, 因为有各种其它条件会阻碍 core dump的产生:

  1. 系统设置了 core size 的大小太小, 查看 ulimit -c
  2. core dump 要写入的文件夹没有权限
  3. core dump 被系统设置拦截, 比如 apport, 它产生了 crash report, 却拦截了 core dump的产生.

core dump 有什么用?

core dump 里有进程当时时间点上的全部内存信息, 寄存器信息, 栈信息, 栈上变量值, 打开文件句柄, 打开的socket 等各种非常有用的信息, 对于诊断应用为什么崩溃具有很大的意义.

gdb 可以直接打开core dump 文件, 并读取里面的信息.

$ gdb core core.720444` 

使用 gdb 去debug Java 程序比较麻烦.

但是可以使用 JDK 自带的 jdb:

# java 8 版本
$ jdb -listconnectors
$ jdb -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:javaExecutable=$JAVA_HOME/bin/java,core=core.720444

java 11 版本
$ jhsdb clhsdb --exe ErrorExample --core core.720444

Core Dump vs Java Heap Dump

Core Dump

  • 定义
    Core dump 是操作系统在进程异常终止时生成的一个文件,它包含了进程终止时内存中的内容。
  • 内容
    Core dump 包含了进程的整个内存映像,包括程序计数器、寄存器、堆栈、堆内存、全局变量、打开的文件描述符、环境变量、程序代码、加载的共享库等。
  • 用途
    主要用于程序崩溃后的调试和故障排查。可以使用调试工具(如 gdb)来分析 core dump 文件,确定程序崩溃的原因。
  • 大小
    Core dump 文件通常很大,因为它包含了整个进程的内存映像。
  • 生成方式
    Core dump 通常由操作系统在进程崩溃时自动生成,或者可以使用 gcore 命令手动生成。

Java Heap Dump

  • 定义
    Java heap dump 是 JVM 在某一时刻的堆内存的快照,包含了所有的 Java 对象和类信息。
  • 内容
    Heap dump 专注于 Java 堆内存,包括对象实例、数组、类实例、垃圾收集器信息等。
  • 用途
    主要用于分析 Java 应用程序的内存使用情况,如检测内存泄漏、查看对象的分配和引用情况等。
  • 大小
    Heap dump 文件的大小取决于 Java 堆的大小,通常比完整的 core dump 小。
  • 生成方式
    Heap dump 可以通过 JVM 提供的工具(如 jmap)、管理接口(如 JMX)生成,或在发生 OutOfMemoryError 时自动生成(如果配置了 -XX:+HeapDumpOnOutOfMemoryError JVM 参数)。

heap dump vs crash log file

这就类似
core dump -> apport crash report
heap dump -> crash log file (err_log_pid.log)

-XX:OnError 选项

如果启动参数配置了-XX:OnError选项, 当 fatal error 产生的时候, JVM 就会执行该选项配置的命令. 多个执行命令可以用 ; 分割开. 可以用 %p 指代当前进程号, 因为 % 用作了特殊符, 所以遇到真的 %, 就要用两个 %% 代替. 官方给的例子:

java -XX:OnError="pmap %p" MyApp #使用 pmap 查看内存空间
java -XX:OnError="gcore %p; dbx - %p" MyApp # 使用 gcore 产生core dump 并用 dbx 进行debug
java -XX:OnError="gdb - %p" MyApp # 使用 gdb debug

-XX:ErrorFile 选项

设置当 fatal error 产生的时候, 把log写到哪个文件去, 需要全路径名(%p指代当前进程号, %%=%).
如果该文件存在, 并且可以写, 那么就覆盖.
如果不设置, 它的默认行为是: 产生一个 java_error_%p.log 文件, %p是进程号, 默认放在当前进程的当前工作目录(CWD), 如果当前工作目录不可用(比如写权限,空间不够等), 会写到临时文件夹目录(/tmp).
参考: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/felog001.html

试验 -XX:OnError 和 -XX:ErrorFile

更多细节在:
https://www.tianxiaohui.com/index.php/Troubleshooting/Java-%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-fatal-error.html

标签: none

添加新评论