java进程coredump(二)

点击量:768

上篇介绍了如何生成一个Java进程的coredump,今天则来总结下当Java进程挂掉之后应该怎么样一步一步来分析。

一、Fatal Error Log
接着昨天的例子继续看,在coredump之后目录里面的文件是这样的:

一般情况下当Java进程挂掉之后,在程序目录下面会生成一个错误日志:hs_err_pidxxxx.log。这个文件是你排查问题的过程中第一个应该阅读的日志,因为这个文件里面会记录很多有用的信息,比如:线程堆栈,堆的使用情况,JVM参数和环境变量,虚拟机状态,操作系统信息等。这里提供的信息非常多,也非常全面,为我们大致定位问题提供了很大的帮助。
先来看下文件开头的注释信息,这里的信息很粗略地指出了崩溃的原因:

第8,9行指出了出现问题的栈是在动态库crash.so的Java_CoreDumpTest_crash方法。接下来看下具体的堆栈信息:

第一段Native frames包含了所有代码的堆栈信息,上限是一百行。这里可以清楚的看到程序在执行CoreDumpTest.crash()之后调用本地方法crash.so里的Java_CoreDumpTest_crash方法而崩溃的,第二段的Java frames仅仅包含Java代码的堆栈。经过这两段分析可以简单定位问题。

二、GDB调试

GDB只提供了native code层面的堆栈信息,再往上的堆栈函数名都变成问号了!!这是为什么呢?注意到第11行打印出了“Reading symbols from /usr/local/jdk/jre/bin/java…(no debugging symbols found)…done.”,就是说从可执行文件/usr/local/jdk/jre/bin/java中并没有读取到符号表,学过编译原理的应该知道,函数名,变量名等都是存储在符号表里的,如果没有了符号表,那么函数名也就无法解析了。OK,那再思考一下,为什么上一篇文章里GDB调试C代码的时候就能打印出函数名,就能读取到符号表呢?为什么Java代码就不行呢?我们注意到,上文调试的可执行文件是test,就是我们运行报错的程序,但这里却是Java,Java里面自然是不会包括你写的程序的符号表了(因为符号表存储在自己写的Java程序中),那为什么不直接用GDB调试自己写的Java程序呢?因为Java程序只能在jvm上面跑,并不能被操作系统直接执行。分析到这里我们可以发现,因为打印不出Java层面的堆栈,GDB调试对Java coredump的分析作用并不大。举个例子,比如报错出现在这一行 in Java_java_net_SocketInputStream_socketRead0 () from /usr/local/jdk/jre/lib/amd64/libnet.so 这是网络读包的本地函数,上层调用这个函数的地方太多了,如果抓不到上层的Java堆栈,那么对问题的定位不会有太大的帮助。

三、jstack
当然还是有办法获得获得Java层面的堆栈信息的,比如可以通过强大的调试工具:jstack来调试core文件。
1).直接使用jstack

2).使用jsadebugd
jsadebugd可以依附到一个Java进程或者core文件,充当一个调试服务器的作用。客户端的一些工具比如jstack,jmap,jinfo可以通过RMI连接到这个服务器进行调试。

然后jstack:

后面的堆栈信息和直接使用jstack是一样的,不再赘述。

四、jmap
jstack能分析core文件,那么jmap也是可以的。

这里指出一点:不论是使用jsadebugd还是直接使用jstack,jmap调试core文件,在JDK 1.6.0_22版本下都会报错!换成1.7的JDK之后就好了,不知道是不是JDK版本小于1.7的都有问题,还是只是1.6的这个版本有问题,还是哪里设置不正确?要把这个问题弄清楚需要的条件实在太多了,不再一一尝试。

报错信息如下:

报错时采用的JDK版本信息如下:

参考:
jsadebugd
Fatal Error Log

发表评论

电子邮件地址不会被公开。 必填项已用*标注