关闭

jvm crash 的崩溃日志详细分析及注意点

标签: crashjvmthreadlinuxreportjava
22899人阅读 评论(2) 收藏 举报
分类:

生成

1. 生成error 文件的路径:你可以通过参数设置-XX:ErrorFile=/path/hs_error%p.log, 默认是在java运行的当前目录 [default: ./hs_err_pid%p.log]

2. 参数-XX:OnError  可以在crash退出的时候执行命令,格式是-XX:OnError=“string”,  <string> 可以是命令的集合,用分号做分隔符, 可以用"%p"来取到当前进程的ID.
例如:

// -XX:OnError="pmap %p"                // show memory map

// -XX:OnError="gcore %p; dbx - %p"     // dump core and launch debugger

在linux中系统会fork出一个子进程去执行shell的命令,因为是用fork可能会内存不够的情况,注意修改你的 /proc/sys/vm/overcommit_memory 参数,不清楚为什么这里不使用vfork

3. -XX:+ShowMessageBoxOnError 参数,当jvm crash的时候在linux里会启动gdb 去分析和调式,适合在测试环境中使用。


什么情况下不会生成error文件

linux 内核在发生OOM的时候会强制kill一些进程, 可以在/var/logs/messages中查找


Error crash 文件的几个重要部分

a.  错误信息概要

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000000043566, pid=32046, tid=1121192256
#
# JRE version: 6.0_17-b04
# Java VM: Java HotSpot(TM) 64-Bit Server VM (14.3-b01 mixed mode linux-amd64 )
# Problematic frame:
# C  0x0000000000043566
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

SIGSEGV 错误的信号类型 

pc 就是IP/PC寄存器值也就是执行指令的代码地址

pid 就是进程id

# Problematic frame:
# V [libjvm.so+0x593045]

就是导致问题的动态链接库函数的地址

pc 和 +0x593045 指的是同一个地址,只是一个是动态的偏移地址,一个是运行的虚拟地址



b.信号信息

Java中在linux 中注册的信号处理函数,中间有2个参数info, ucvoid

static void crash_handler(int sig, siginfo_t* info, void* ucVoid) {
  // unmask current signal
  sigset_t newset;
  sigemptyset(&newset);
  sigaddset(&newset, sig);
  sigprocmask(SIG_UNBLOCK, &newset, NULL);

  VMError err(NULL, sig, NULL, info, ucVoid);
  err.report_and_die();
}
在crash report中的信号错误提示

siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x0000000000043566
信号的详细信息和si_addr 出错误的内存,都保存在siginfo_t的结构体中,也就是信号注册函数crash_handler里的参数info,内核会保存导致错误的内存地址在用户空间的信号结构体中siginfo_t,这样在进程在注册的信号处理函数中可以取得导致错误的地址。


c.寄存器信息

Registers:
RAX=0x00002aacb5ae5de2, RBX=0x00002aaaaf46aa48, RCX=0x0000000000000219, RDX=0x00002aaaaf46b920
RSP=0x0000000042d3f968, RBP=0x0000000042d3f9c8, RSI=0x0000000042d3f9e8, RDI=0x0000000045aef9b8
R8 =0x0000000000000f80, R9 =0x00002aaab3d30ce8, R10=0x00002aaaab138ea1, R11=0x00002b017ae65110
R12=0x0000000042d3f6f0, R13=0x00002aaaaf46aa48, R14=0x0000000042d3f9e8, R15=0x0000000045aef800
RIP=0x0000000000043566, EFL=0x0000000000010202, CSGSFS=0x0000000000000033, ERR=0x0000000000000014
  TRAPNO=0x000000000000000e

寄存器的信息就保存在b部分的信号处理函数参数 (ucontext_t*)usVoid中

在X86架构下:

void os::print_context(outputStream *st, void *context) {
  if (context == NULL) return;

  ucontext_t *uc = (ucontext_t*)context;
  st->print_cr("Registers:");
#ifdef AMD64
  st->print(  "RAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RAX]);
  st->print(", RBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBX]);
  st->print(", RCX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RCX]);
  st->print(", RDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDX]);
  st->cr();
  st->print(  "RSP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSP]);
  st->print(", RBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RBP]);
  st->print(", RSI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSI]);
  st->print(", RDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDI]);
  st->cr();
  st->print(  "R8 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]);
  st->print(", R9 =" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R9]);
  st->print(", R10=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R10]);
  st->print(", R11=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R11]);
  st->cr();
  st->print(  "R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]);
  st->print(", R13=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R13]);
  st->print(", R14=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R14]);
  st->print(", R15=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R15]);
  st->cr();
  st->print(  "RIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RIP]);
  st->print(", EFL=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]);
  st->print(", CSGSFS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_CSGSFS]);
  st->print(", ERR=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ERR]);
  st->cr();
  st->print("  TRAPNO=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_TRAPNO]);
#else
  st->print(  "EAX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EAX]);
  st->print(", EBX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBX]);
  st->print(", ECX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ECX]);
  st->print(", EDX=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDX]);
  st->cr();
  st->print(  "ESP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_UESP]);
  st->print(", EBP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EBP]);
  st->print(", ESI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_ESI]);
  st->print(", EDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EDI]);
  st->cr();
  st->print(  "EIP=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EIP]);
  st->print(", CR2=" INTPTR_FORMAT, uc->uc_mcontext.cr2);
  st->print(", EFLAGS=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_EFL]);
#endif // AMD64
  st->cr();
  st->cr();

  intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc);
  st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp);
  print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t));
  st->cr();

  // Note: it may be unsafe to inspect memory near pc. For example, pc may
  // point to garbage if entry point in an nmethod is corrupted. Leave
  // this at the end, and hope for the best.
  address pc = os::Linux::ucontext_get_pc(uc);
  st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc);
  print_hex_dump(st, pc - 16, pc + 16, sizeof(char));
}
寄存器的信息在分析出错的时候是非常重要的

打印出执行附近的部分机器码

Instructions: (pc=0x00007f48f14ef51a)
0x00007f48f14ef4fa:   90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd
0x00007f48f14ef50a:   f8 5f ff ff 48 89 b5 f0 5f ff ff b8 00 00 00 00
0x00007f48f14ef51a:   c7 00 01 00 00 00 c6 85 00 60 ff ff ff c9 c3 90
0x00007f48f14ef52a:   90 90 90 90 90 90 55 48 89 e5 53 48 8d 1d 94 00

在instruction 部分中会打印出部分的机器码

格式是

地址:机器码

第一种使用udis库里带的udcli工具来反汇编

命令:

echo '90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd' | udcli -intel -x -64 -o 0x00007f48f14ef4fa

显示出对应的汇编

第二种可以用

objectdump -d -C libjvm.so >> jvmsodisass.dump 

查找偏移地址  0x593045, 就是当时的执行的汇编,然后结合上下文,源码推测出问题的语句。


d.寄存器对应的内存的值

RAX=0x0000000000000000 is an unknown value
RBX=0x000000041a07d1e8 is an oop
{method}
 - klass: {other class}
RCX=0x0000000000000000 is an unknown value
RDX=0x0000000040111800 is a thread
RSP=0x0000000041261b88 is pointing into the stack for thread: 0x0000000040111800
RBP=0x000000004126bb20 is pointing into the stack for thread: 0x0000000040111800
RSI=0x000000004126bb80 is pointing into the stack for thread: 0x0000000040111800
RDI=0x00000000401119d0 is an unknown value
R8 =0x0000000040111c40 is an unknown value
R9 =0x00007f48fcc8b550: <offset 0xa85550> in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000
R10=0x00007f48f8ca7d41 is an Interpreter codelet
method entry point (kind = native)  [0x00007f48f8ca7ae0, 0x00007f48f8ca8320]  2112 bytes
R11=0x00007f48fc98f270: <offset 0x789270> in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000
R12=0x0000000000000000 is an unknown value
R13=0x000000041a07d1e8 is an oop
{method}
 - klass: {other class}
R14=0x000000004126bb88 is pointing into the stack for thread: 0x0000000040111800
R15=0x0000000040111800 is a thread
jvm 会通过寄存器的值对找对应的对象,也是一个比较好的参考


e. 其他的信息

error 里面还有一些线程信息,还有当时内存映像信息,这些都可以作为分析的部分参考


crash 报告可以大概的反应出一个当时的情况,特别是在没有core dump的时候,是比较有助于帮助分析的,但如果有core dump的话,最终还是core dump能快速准确的发现问题原因。









3
0
查看评论

如何抓获JVM crash的幕后黑手?(一)

最近几天线上jboss服务器经常莫名地突然停止运行,导致半夜都被报警短信吵醒,元旦几天也基本就在收报警,然后重启系统。查看jboss控制台错误日志,发现只有下面一行: /opt/.../jboss/bin/run.sh: line 181: 26430 段错误   &...
  • cpzhong
  • cpzhong
  • 2012-01-11 14:35
  • 16493

热替换jar包导致JVM崩溃(crash)

情景 在一次项目升级过程中,需要替换一个jar包,已经有进程用这个jar包启动运行了。 操作 当没有终止这些进程的情况下,热替换该jar包 现象 替换jar包之后,在运行的进程全部消失了,打印出jvm crash的日志。 原因 查找相关资料后发现是因为java读取jar包时,为了提高性...
  • jgteng
  • jgteng
  • 2017-03-17 18:10
  • 670

JVM Crash, 求如何分析原因.[已分析,和提出了解决方案]

JDK 1.5.0_20  下面是Crash Log, 想知道详细问题, 和如何分析,调试:  (分析看2, 4, 6楼. 解决方案看6楼, 8楼有方案2的性能损耗测试)  Java代码   # ...
  • u013339596
  • u013339596
  • 2014-01-20 18:54
  • 2200

JVM crash log

# # There is insufficient memory for the Java Runtime Environment to continue. # Native memory allocation (mmap) failed to map 45531136 bytes for comm...
  • qxd100
  • qxd100
  • 2017-01-20 17:00
  • 645

JVM Crash

JVM Crash上一篇 / 下一篇  2008-01-15 18:58:00查看( 1204 ) / 评论( 78 ) / 评分( 12 / 0 )如果是Java进程不知道什么原...
  • antswallow
  • antswallow
  • 2009-12-09 17:28
  • 1169

jvm crash 分析

今天tomcat的jvm崩了,一直jvm崩掉,每次也就看了一下内存区域,判断大概是内存问题,今天决定好好分析一下,整理了网上的一些资料。     1.crash文件      如果有一个严重的错误引起...
  • y666666y
  • y666666y
  • 2017-04-17 16:11
  • 215

JVM崩溃日志分析1

最先线上的一个tomcat总是无缘无故崩溃,tomcat日志里又没有报任何错误,于是调出JVM的崩溃日志查看,一般崩溃日志在启动目录下,比如tomcat的bin目录下,但是如果你用自己写的脚本启动的tomcat,则这个日志可能就在你放脚本的目录下。 # # A fatal error has bee...
  • achilles12345
  • achilles12345
  • 2016-03-14 16:37
  • 5846

JVM崩溃日志分析2,没有生成hs_err_<pid>.log日志原因分析

tomcat崩溃了,但是也没有生成hs_err_.log日志,但是生成了core日志,tomat的日志下边只报了一下边的错误:        #       # A fatal error has been detected by...
  • achilles12345
  • achilles12345
  • 2016-03-14 17:18
  • 2580

JVM -XX: 参数介绍

功能开关: 参数 默认值或限制 说明 参数 默认值 功能 -XX:-AllowUserSignalHandlers 限于Linux和Solaris,默认不启用 允许为java进程安装信号处理器,信号处理参见类:sun....
  • xyw591238
  • xyw591238
  • 2016-07-14 17:37
  • 2913

JVM--内存参数设置及常见错误总结

一,JVM规范 JVM规范对Java运行时的内存划定了几块区域(详见这里),有:JVM栈(Java Virtual Machine Stacks)、堆(Heap)、方法区(Method Area)、常量池(Runtime Constant Pool)、本地方法栈(Native Method Sta...
  • szzt_lingpeng
  • szzt_lingpeng
  • 2016-01-06 08:28
  • 9390
    个人资料
    • 访问:500018次
    • 积分:5446
    • 等级:
    • 排名:第5866名
    • 原创:100篇
    • 转载:3篇
    • 译文:0篇
    • 评论:73条
    最新评论