当jvm挂掉的时候。jvm会生成一个带有hs_err_pid.log这样的文件。pid后面跟着的是当前虚拟机的进程id。
在这个文件中,我们往往能够查看到虚拟机挂掉的很多重要信息。所以学会如何分析hs_err_pid文件将能够提高我们定位问题的能力。
也可以通过 java -XX: ErrorFile = /var/log/java/java_error&p.log 的参数进行指定。
这个文件主要分为以下内容:
1、触发知名错误的操作异常或信号;
2、版本和配置信息;
3、触发致命异常的线程详细信息和线程栈;
4、当前运行的线程列表和状态;
5、堆信息;
6、加载的本地动态库信息(或so);
7、命令行参数;
8、环境变量;
9、操作系统的详细信息以及cpu信息。
接下来我们根据此文件进行逐段分析。
一个虚拟机的非预期的错误被JRE检查到,其中:
SIGSEGV 是信号名称
0xb 是信号码
pc=0x00007f8076a3ffee指的是程序计数器的值
pid=12252 指进程号
pc=0x00007f8076a3ffee 指的是线程号
文件头中有很多有用的信息,“EXCEPTION_ACCESS_VIOLATION ”意味着Java应用Crash的时候,正在运行JVM自己的代码,而不是外部的Java代码或其他类库代码。
这种情况很可能是JVM的Bug,但是也不一定。除了“EXCEPTION_ACCESS_VIOLATION ”,还有可能是别的信息,例如“SIGSEGV(0xb)”,意味着JVM正在执行本地或JNI的代码;“EXCEPTION_STACK_OVERFLOW”意味着这是个栈溢出的错误。
这里是jre和java 虚拟机的版本信息以及运行环境。
这里是出现问题的问题帧。最下面一行是需要进行coredump时(C/C++语言)产生coredump文件需要执行的操作,(这部分之后我会再研究一下。不太清楚c的分析过程)。
C:是本地帧类型
帧的类型包括:
1、C:本地帧
2、j :解释的java帧
3、V(大写):虚拟机帧
4、v (小写):虚拟机生成的存根栈帧
5、J (大写) :其它帧类型,包括编译后的java帧
6、[libdhnetsdk.so+0xf50fee : 和程序计数器(pc)表达的含义一样,但是用的是本地so库+偏移量的方法。
0x00007f80a9aa5800 线程指针
javaThread 是线程的类型,这里明显的表达是java线程:
其它类型:
- JavaThread
- VMThread
- CompilerThread
- GCTaskThread
- WatcherThread
- ConcurrentMarkSweepThread
SimpleAsyncTaskExecutor-2 :线程名
- _thread_in_native:线程当前状态,状态枚举包括:
- _thread_uninitialized:线程还没有创建,它只在内存原因崩溃的时候才出现
- _thread_new:线程已经被创建,但是还没有启动
- _thread_in_native:线程正在执行本地代码,一般这种情况很可能是本地代码有问题
- _thread_in_vm:线程正在执行虚拟机代码
- _thread_in_Java:线程正在执行解释或者编译后的Java代码
- _thread_blocked:线程处于阻塞状态
- …_trans:以_trans结尾,线程正处于要切换到其它状态的中间状态
id =12617 :线程id
0x00007f80855a8000,0x00007f80856a9000 : 线程栈区间
这部分是导致虚拟机终止的非预期的信号信息,含义前面已经大致提到过了。其中si_errno和si_code是Linux下用来鉴别异常的,Windows下是一个ExceptionCode。
这是寄存器的上下文。
栈顶程序计数器旁的操作码,它们可以被反汇编成系统崩溃前执行的指令。(我只拷贝的一部分)
寄存器和内存映射信息。
线程栈。包含了地址、栈顶、栈计数器和线程尚未使用的栈信息,由于栈可能非常长,打印的长度有限制,
但是至少本地栈和Java栈都打印出来了(很多时候本地栈打印不出来,但是Java栈一般都能打印出来)。
线程信息,就不赘述了。
虚拟机状态。包括:
- not at a safepoint:正常运行状态;
- at safepoint:所有线程都因为虚拟机等待状态而阻塞,等待一个虚拟机操作完成;
- synchronizing:一个特殊的虚拟机操作,要求虚拟机内的其它线程保持等待状态。
虚拟机的Mutex和Monitor目前没有被线程持有。Mutex是虚拟机内部的锁,而Monitor则关联到了Java对象。
接下来的一些信息是关于堆信息。新生代、老生代、永久代。对JVM有了解的人应该都清楚,不解释了。
内存映射。这些信息是虚拟机崩溃时的虚拟内存列表区域。在定位崩溃原因的时候,它可以告诉你哪些类库正在被使用,位置在哪里,还有堆栈和守护页信息。
00400000-00401000 :内存区域
r-xp :权限,r/w/x/p/s分别表示读/写/执行/私有/共享
00000000:文件内的偏移量
fd:00:文件位置的majorID和minorID
68351097:索引节点号
/usr/java/jdk1.8.0_162/jre/bin/java:文件位置
每一个lib都有两块虚拟内存区域——代码和数据,它们的权限不同,代码区域是r-xp;数据区域是rwxp。守护页(guard page)由权限为–xp和rwxp的一对组成。
虚拟机参数和环境变量。
信号句柄。对于Linux下的信号机制,请自行百度。
代码缓存(Code Cache)。这是一块用于编译和保存本地代码的内存,注意是本地代码,它和PermGen(永久代)是不一样的,永久带是用来存放Java类定义的。
最后是系统信息。