最近两天测试环境有一个服务总是会挂(两到三天一次),JVM虚拟机总是会崩溃。所以有必要了解JVM崩溃的原因是什么。
当JVM发生致命错误导致崩溃时,会生成一个hs_err_pid_xxx.log这样的文件,该文件包含了导致 JVM crash 的重要信息,我们可以通过分析该文件定位到导致 JVM Crash 的原因,从而修复保证系统稳定。
默认情况下,该文件是生成在工作目录下的,当然也可以通过 JVM 参数指定生成路径:
-XX:ErrorFile=/var/log/hs_err_pid<pid>.log这个文件主要包含如下内容:
日志头文件导致 crash 的线程信息所有线程信息安全点和锁信息堆信息本地代码缓存编译事件gc 相关记录jvm 内存映射jvm 启动参数服务器信息下面就根据这个文件内容逐步解析。
内容如下
# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x0000003797807a91, pid=29071, tid=139901421901568 # # JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libresolv.so.2+0x7a91] __libc_res_nquery+0x1c1 # # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # # If you would like to submit a bug report, please visit: # http://bugreport.java.com/bugreport/crash.jsp # The crash happened outside the Java Virtual Machine in native code. # See problematic frame for where to report the bug. #这段内容主要简述了导致 JVM Crash 的原因。常见的原因有 JVM 自身的 bug,应用程序错误,JVM 参数,服务器资源不足,JNI 调用错误等。当然还有一些版本和配置信息,
SIGSEGV (0xb) at pc=0x0000003797807a91, pid=29071, tid=139901421901568非预期的错误被 JRE 检测到了,其中
SIGSEGV :信号量0xb :信号码pc=0x0000003797807a91 :程序计数器的值pid=29071 :进程号tid=139901421901568 :线程号SIGSEGV(0xb) 表示 JVM Crash 时正在执行 JNI 代码,常见的描述还有EXCEPTION_ACCESS_VIOLATION,该描述表示 JVM Crash 时正在执行 JVM 自身的代码,这往往是因为 JVM 的 Bug 导致的 Crash;另一种常见的描述是EXCEPTION_STACK_OVERFLOW,该描述表示这是个栈溢出导致的错误,这往往是应用程序中存在深层递归导致的。
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode linux-amd64 compressed oops)这个信息比较重要,问题帧信息:
C 表示帧类型为本地帧,还有其他类型:
j : 解释的Java帧V : 虚拟机帧v :虚拟机生成的存根栈帧J:其他帧类型,包括编译后的Java帧[libresolv.so.2+0x7a91] __libc_res_nquery+0x1c1和程序计数器(pc)表达的含义一样,但是用的是本地so库+偏移量的方式。
内容如下:
--------------- T H R E A D --------------- Current thread (0x0000000001e94800): JavaThread "pool-1-thread-2" [_thread_in_native, id=30111, stack(0x00007f3d567e5000,0x00007f3d568e6000)] siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000003 Registers: RAX=0x0000000000000000, RBX=0x0000000000000000, RCX=0x0000000000000000, RDX=0x0000000000000050 RSP=0x00007f3d568e2280, RBP=0x00007f3d568e2570, RSI=0x0000000000000000, RDI=0x00000000ffffffff R8 =0x0000000000000000, R9 =0x0000000000000000, R10=0x000000000007a337, R11=0x0000000000000213 R12=0x00007f3d568e2ef0, R13=0x00007f3d568e22b0, R14=0x0000000000000000, R15=0x00007f3d568e5db8 RIP=0x0000003797807a91, EFLAGS=0x0000000000010246, CSGSFS=0x0000000000000033, ERR=0x0000000000000004 TRAPNO=0x000000000000000e Top of Stack: (sp=0x00007f3d568e2280) 0x00007f3d568e2280: b8e4bfb900000800 00007f3d568e3760 0x00007f3d568e2290: 00007f3d568e3758 00007f3d568e377c 0x00007f3d568e22a0: 00007f3d568e3778 6f6e6b6e56a88a58 0x00007f3d568e22b0: 00000100000149a0 7a68710800000000 0x00007f3d568e22c0: 6970067363642d78 6d6f63036e61676e ....省略 Instructions: (pc=0x0000003797807a91) 0x0000003797807a71: 48 89 45 b8 48 8b 4d b8 0f b6 51 03 89 d3 83 e3 0x0000003797807a81: 0f 75 0d 0f b7 49 06 66 c1 c9 08 66 85 c9 75 4f 0x0000003797807a91: 0f b6 48 03 bf 0f 00 00 00 40 20 cf 75 0d 0f b7 0x0000003797807aa1: 70 06 66 c1 ce 08 66 85 f6 75 34 83 e1 0f 83 e2 Register to memory mapping: RAX=0x0000000000000000 is an unknown value RBX=0x0000000000000000 is an unknown value RCX=0x0000000000000000 is an unknown value RDX=0x0000000000000050 is an unknown value RSP=0x00007f3d568e2280 is pointing into the stack for thread: 0x0000000001e94800 RBP=0x00007f3d568e2570 is pointing into the stack for thread: 0x0000000001e94800 ... 省略 Stack: [0x00007f3d567e5000,0x00007f3d568e6000], sp=0x00007f3d568e2280, free space=1012k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [libresolv.so.2+0x7a91] __libc_res_nquery+0x1c1 C [libresolv.so.2+0x7fd1] Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) J 15056 java.net.Inet6AddressImpl.lookupAllHostAddr(Ljava/lang/String;)[Ljava/net/InetAddress; (0 bytes) @ 0x00007f3d7492af8c [0x00007f3d7492af40+0x4c] J 14966 C1 java.net.InetAddress.getAddressesFromNameService(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress; (245 bytes) @ 0x00007f3d75466754 [0x00007f3d754662c0+0x494] J 14291 C2 java.net.InetAddress.getAllByName(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress; (387 bytes) @ 0x00007f3d7534b718 [0x00007f3d7534ae20+0x8f8] J 14178 C1 java.net.InetSocketAddress.<init>(Ljava/lang/String;I)V (47 bytes) @ 0x00007f3d752ce0f4 [0x00007f3d752cdec0+0x234] j sun.security.ssl.SSLSocketImpl.<init>(Lsun/security/ssl/SSLContextImpl;Ljava/lang/String;ILjava/net/InetAddress;I)V+144 j sun.security.ssl.SSLSocketFactoryImpl.createSocket(Ljava/lang/String;ILjava/net/InetAddress;I)Ljava/net/Socket;+13 j com.ufclub.daq.qhzx.utils.SSLProtocolSocketFactory.createSocket(Ljava/lang/String;ILjava/net/InetAddress;I)Ljava/net/Socket;+15 .... 省略代码这部分内容包含出发 JVM 致命错误的线程详细信息和线程栈。
表示导致虚拟机终止的非预期的信号信息
Top of Stack: (sp=0x00007f3d568e2280) 0x00007f3d568e2280: b8e4bfb900000800 00007f3d568e3760 0x00007f3d568e2290: 00007f3d568e3758 00007f3d568e377c 0x00007f3d568e22a0: 00007f3d568e3778 6f6e6b6e56a88a58 0x00007f3d568e22b0: 00000100000149a0 7a68710800000000 0x00007f3d568e22c0: 6970067363642d78 6d6f63036e61676e ....省略 Instructions: (pc=0x0000003797807a91) 0x0000003797807a71: 48 89 45 b8 48 8b 4d b8 0f b6 51 03 89 d3 83 e3 0x0000003797807a81: 0f 75 0d 0f b7 49 06 66 c1 c9 08 66 85 c9 75 4f 0x0000003797807a91: 0f b6 48 03 bf 0f 00 00 00 40 20 cf 75 0d 0f b7 0x0000003797807aa1: 70 06 66 c1 ce 08 66 85 f6 75 34 83 e1 0f 83 e2栈顶程序计数器旁的操作码,它们可以被反汇编成系统崩溃前执行的指令。
Stack: [0x00007f3d567e5000,0x00007f3d568e6000], sp=0x00007f3d568e2280, free space=1012k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [libresolv.so.2+0x7a91] __libc_res_nquery+0x1c1 C [libresolv.so.2+0x7fd1] Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) J 15056 java.net.Inet6AddressImpl.lookupAllHostAddr(Ljava/lang/String;)[Ljava/net/InetAddress; (0 bytes) @ 0x00007f3d7492af8c [0x00007f3d7492af40+0x4c] J 14966 C1 java.net.InetAddress.getAddressesFromNameService(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress; (245 bytes) @ 0x00007f3d75466754 [0x00007f3d754662c0+0x494] J 14291 C2 java.net.InetAddress.getAllByName(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress; (387 bytes) @ 0x00007f3d7534b718 [0x00007f3d7534ae20+0x8f8] J 14178 C1 java.net.InetSocketAddress.<init>(Ljava/lang/String;I)V (47 bytes) @ 0x00007f3d752ce0f4 [0x00007f3d752cdec0+0x234] j sun.security.ssl.SSLSocketImpl.<init>(Lsun/security/ssl/SSLContextImpl;Ljava/lang/String;ILjava/net/InetAddress;I)V+144 j sun.security.ssl.SSLSocketFactoryImpl.createSocket(Ljava/lang/String;ILjava/net/InetAddress;I)Ljava/net/Socket;+13 j com.ufclub.daq.qhzx.utils.SSLProtocolSocketFactory.createSocket(Ljava/lang/String;ILjava/net/InetAddress;I)Ljava/net/Socket;+15 .... 省略代码线程栈信息。包含了地址、栈顶、栈计数器和线程尚未使用的栈信息。到这里就基本上已经确定了问题所在原因了。
所有线程信息,一目了然。_thread_blocked表示阻塞。
虚拟机状态。not at safepoint 表示正常运行。其余状态:
at safepoint:所有线程都因为虚拟机等待状态而阻塞,等待一个虚拟机操作完成;synchronizing:一个特殊的虚拟机操作,要求虚拟机内的其它线程保持等待状态。 VM Mutex/Monitor currently owned by a thread: None虚拟机的 Mutex 和 Monito r目前没有被线程持有。Mutex 是虚拟机内部的锁,而 Monitor 则关联到了 Java 对象。
新生代、老年代、元空间一目了然。
Card table表示一种卡表,是 jvm 维护的一种数据结构,用于记录更改对象时的引用,以便 gc 时遍历更少的 table 和 root。
一块用于编译和保存本地代码的内存。
记录10次编译事件。这里的信息也印证了上面的结论。
同样是记录10次 GC。
这些信息是虚拟机崩溃时的虚拟内存列表区域。它可以告诉你崩溃原因时哪些类库正在被使用,位置在哪里,还有堆栈和守护页信息。
00400000-00401000:内存区域r-xp:权限,r/w/x/p/s分别表示读/写/执行/私有/共享00000000:文件内的偏移量fd:00:文件位置的majorID和minorID2108521:索引节点号/usr/java/jdk1.8.0_45/bin/java:文件位置 从/opt/risk/service/xxx-xxx-container/xxx-xxxx-container.jar我们可以确认是那个jar出问题了。jvm 虚拟机参数和环境变量。