[翻译]Java排错指南 - 5 确定崩溃何地发生
原文地址: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/crashes001.html
这几天公司其他组遇到了一个segmentation fault的问题,找到了这个官方文档,基于Java8,感觉不错就翻译了下.
一些地方翻译比较生硬,如有问题请麻烦指正~_ by fairjm
5.1 确定崩溃何地发生
这一节提供了一些例子来演示如何使用错误日志来找到崩溃的原因,并且给出一些排查这些问题的建议.
错误日志的头指出了错误的类型和有问题的帧(frame),thread stack指出了当前的线程和堆栈轨迹.查看Header Format
Crash in Native Code
Crash in Compiled Code
Crash in HotSpot Compiler Thread
Crash in VM Thread
Crash Due to Stack Overflow
5.1.1 本地代码崩溃
如果致命错误日志(fatal error log)指出的问题帧来自于本地库,那么可能是本地库或者JNI库代码存在bug.这种崩溃当然也有可能是其他原因造成的,但是分析这个库和其他的core file或者crash dump是一个很好的开始.
以下是一个致命错误日志的头:
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# SIGSEGV (0xb) at pc=0x417789d7, pid=21139, tid=1024
#
# Java VM: Java HotSpot(TM) Server VM (6-beta2-b63 mixed mode)
# Problematic frame:
# C [libApplication.so+0x9d7]
在这个例子中,SIGSEGV
发生在线程执行libApplication.so
中的代码.
一些其他例子是Java VM的本地库导致的.在下面的例子中,JavaThread
在_thread_in_vm
状态中失败了(表明它正在执行Java VM代码)
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3700, tid=2896
#
# Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode)
# Problematic frame:
# V [jvm.dll+0x83d77]
--------------- T H R E A D ---------------
Current thread (0x00036960): JavaThread "main" [_thread_in_vm, id=2896]
:
Stack: [0x00040000,0x00080000), sp=0x0007f9f8, free space=254k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [jvm.dll+0x83d77]
C [App.dll+0x1047] <========= C/native frame
j Test.foo()V+0
j Test.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
V [jvm.dll+0x80f13]
V [jvm.dll+0xd3842]
V [jvm.dll+0x80de4]
V [jvm.dll+0x87cd2]
C [java.exe+0x14c0]
C [java.exe+0x64cd]
C [kernel32.dll+0x214c7]
:
在这个例子中,虽然出问题的帧是VM的,线程栈显示一个本地例子(native routine)App.dll
已经被VM调用(可能是通过JNI).
解决这个本地库崩溃的第一步是调查本地库发生崩溃的那段源代码.
- 如果本地库是由你的应用程序提供,那么调查本地库的源代码.大量的问题可以通过在运行应用程序时使用
-Xcheck:jni
参数被识别.查看The -Xcheck:jni Option. - 如果这个本地库是由其他供应商提供,被你的程序所使用,那么就向他们提供bug报告和致命错误日志信息.
- 如果本地库是来自于JRE的(比如awt.dll,net.dll或其他的),那有可能你遇到了一个库或者API的bug.那么就尽可能获取足够的信息提交一个bug并指名库名称.你可以在JRE的发行版中的 jre/lib 或 jre/bin目录中找到JRE的库.
如果可能的话,你可以通过本地debugger attach到core file或crash dump的方式来排查本地库崩溃.取决于你所使用的系统,本地debugger有dbx
,gdb
,或windbg
.查看Native Operating System Tools
5.1.2 编译代码崩溃
如果致命错误日志指出崩溃发生在编译代码(compiled code),那有可能你遇到了一个编译器导致的不正确代码生成的bug.你可以通过问题堆栈的类型是J
(代表一个compiled java frame)来识别.
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# SIGSEGV (0xb) at pc=0x0000002a99eb0c10, pid=6106, tid=278546
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (1.6.0-beta-b51 mixed mode)
# Problematic frame:
# J org.foobar.Scanner.body()V
#
:
Stack: [0x0000002aea560000,0x0000002aea660000), sp=0x0000002aea65ddf0,
free space=1015k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J org.foobar.Scanner.body()V
[error occurred during error reporting, step 120, id 0xb]
注意:该例子无法获得一个完整的线程栈.输出的"error occurred during error reporting" 表示问题发生在试图获取堆栈跟踪(可能是栈损坏了).
通过更换编译器的方式可能可以临时解决(例如使用HotSpot Client VM取代HotSpot Server VM,或反过来)或者从编译中排除掉导致崩溃的方法.在这个例子中,将64位的Server VM换成32位的Client VM可能会有用.
更多可能的规避措施见Working Around Crashes in the HotSpot Compiler Thread or Compiled Code.
5.1.3 HotSpot编译器线程崩溃
如果核心错误日志显示的当前线程是一个叫CompilerThread0
,CompilerThread1
或AdapterCompiler
的JavaThread
,那么你可能遇上了编译器bug.可能解决方式和上一节一样.(懒得复读了...)
5.1.4 VM线程崩溃
如果核心错误日志显示的当前线程是VMThread
,那么查看一下在THREAD
那节包含VM_Operation
的那一行.VMThread
是特殊的的HotSpot VM线程.它执行一些特殊的工作比如GC.如果VM_Operation
表明操作是GC,那么你可能遇到了诸如堆损坏的问题.
包括GC问题之外,它同样可能是一些其他问题(例如编译器或者运行时bug)导致的对象引用在堆中处于一个不完整和不正确的状态.这种情况下,收集尽可能多的环境信息和尝试可能的规避方案.如果发现和GC有关,你可能通过修改GC配置的方式来临时保证正常运行.
对于更多的规避方案,查看Working Around Crashes during Garbage Collection
5.1.5 爆栈崩溃
java语言的一个栈溢出通常会导致线程抛出烦人的java.lang.StackOverflowError
异常.另一方面,C和C++写入超过了栈的结束会引起一个栈溢出.这是一个致命的错误,会导致进程终止.
在HotSpot实现中,Java方法和C/C++本地代码共享栈帧,即用户本地代码和VM自身.
Java方法产生的代码会检查栈离栈的结束是否会有固定距离可用的空间,所以本地代码的调用可以不担心是否会超过栈空间.
到栈结束的距离被称为Shadow Pages
.这个大小取决于所在平台,shadow pages在3到20页之间.
这个距离是可以调整的,所以应用使用到了本地代码想要比默认更大的距离时可以增加shadow page的大小.
增加的参数是-XX:StackShadowPages=n
,n设定为比当前平台默认值大.
如果你的应用遇上了segmentation fault但是没有core file或致命错误日志参见Appendix A,或者在windows上STACK_OVERFLOW_ERROR
,或者得到一个消息"An irrecoverable stack overflow has occurred",这说明超过了StackShadowPages
,需要更大的空间.
如果你增加了StackShadowPages
,你可能也需要使用-Xss
参数增加默认的线程栈大小.增加默认的线程栈大小可能会减少可创建的线程数,所以请小心选择这个数值.线程栈的大小于不同平台上在256KB到1024KB.
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x10001011, pid=296, tid=2940
#
# Java VM: Java HotSpot(TM) Client VM (1.6-internal mixed mode, sharing)
# Problematic frame:
# C [App.dll+0x1011]
#
--------------- T H R E A D ---------------
Current thread (0x000367c0): JavaThread "main" [_thread_in_native, id=2940]
:
Stack: [0x00040000,0x00080000), sp=0x00041000, free space=4k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [App.dll+0x1011]
C [App.dll+0x1020]
C [App.dll+0x1020]
:
C [App.dll+0x1020]
C [App.dll+0x1020]
...<more frames>...
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j Test.foo()V+0
j Test.main([Ljava/lang/String;)V+0
v ~StubRoutines::call_stub
你可以从例子中获得以下信息:
- 异常是
EXCEPTION_STACK_OVERFLOW
- 线程的状态是
_thread_in_native
,表示线程在执行native或者JNI代码. - 线程信息中,可用的空间仅仅只有4KB(windows系统中的单页).另外线程指针(sp)在
0x00041000
,很接近栈结束0x00040000
. - 输出的本地栈显示一个递归的本地方法是这个问题的原因.
...<more frames>...
表明还有更多的帧存在但是没有输出.输出只限于100帧.
相关资料:
Do we need Unsafe in Java?
Shortest code that raises a SIGSEGV
Best way on how to solve/debug JVM crash (SIGSEGV)
JVM crash. Problematic frame: J 4518 C2 java.lang.Long.getChar
[翻译]Java排错指南 - 5 确定崩溃何地发生的更多相关文章
- [翻译]Java日志终极指南
本文由 ImportNew - Wing 翻译自 loggly.欢迎加入翻译小组.转载请见文末要求. Java日志基础 Java使用了一种自定义的.可扩展的方法来输出日志.虽然Java通过java.u ...
- [翻译]现代java开发指南 第二部分
现代java开发指南 第二部分 第二部分:部署.监控 & 管理,性能分析和基准测试 第一部分,第二部分 =================== 欢迎来到现代 Java 开发指南第二部分.在第一 ...
- [翻译]现代java开发指南 第一部分
现代java开发指南 第一部分 第一部分:Java已不是你父亲那一代的样子 第一部分,第二部分 =================== 与历史上任何其他的语言相比,这里要排除c语言和cobol语言,现 ...
- [翻译]现代java开发指南 第三部分
现代java开发指南 第三部分 第三部分:Web开发 第一部分,第二部分,第三部分 =========================== 欢迎来到现代 Java 开发指南第三部分.在第一部分中,我们 ...
- 现代java开发指南系列
[翻译]现代java开发指南系列 [翻译]现代java开发指南 第一部分 [翻译]现代java开发指南 第二部分 [翻译]现代java开发指南 第三部分
- Java并发指南8:AQS中的公平锁与非公平锁,Condtion
一行一行源码分析清楚 AbstractQueuedSynchronizer (二) 转自https://www.javadoop.com/post/AbstractQueuedSynchronizer ...
- Java并发指南4:Java中的锁 Lock和synchronized
Java中的锁机制及Lock类 锁的释放-获取建立的happens before 关系 锁是java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消 ...
- 翻译Java虚拟机的结构
英文原版: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 直接谷歌翻译: Java SE规范 > Java虚拟机 ...
- Java并发指南11:解读 Java 阻塞队列 BlockingQueue
解读 Java 并发队列 BlockingQueue 转自:https://javadoop.com/post/java-concurrent-queue 最近得空,想写篇文章好好说说 java 线程 ...
随机推荐
- DD XOFT虚拟键盘鼠标
下载:http://www.ddxoft.com/ 简介:最多用户选择,最简单易用,最稳定可靠 永久免费 系统底层集成,真正的驱动级硬件模拟 一键安装,即为电脑添加一对可完全编程控制的键盘鼠标,轻松自 ...
- 好代码是管出来的——Git的分支工作流与Pull Request
上一篇文章介绍了常用的版本控制工具以及git的基本用法,从基本用法来看git与其它的版本控制工具好像区别不大,都是对代码新增.提交进行管理,可以查看提交历史.代码差异等功能.但实际上git有一个重量级 ...
- BDD测试之selenium控制滚动条
一.对于页面存在滚动条,可以通过插入JS控制滚动条(最常用的方法) (1)将滚动条移动到指定坐标位置处 ((JavascriptExecutor) driver).executeScript(&quo ...
- 深入js隐式类型转换
前言 相信刚开始了解js的时候,都会遇到 2 =='2',但是 1+'2' == '1'+'2'为false的情况,这时候应该会是一脸懵逼的状态,不得不感慨js弱类型的灵活让人发指,隐式类型转换就是这 ...
- 前端BUG监控神器
有时候,看到用户的反馈,我们往往会一脸茫然,因为反馈的信息太少了. 比如有用户反馈登录不了.为了解这个问题,一般的流程是这样的:首先试试自己能不能登录网站,发现没问题:然后查看后台日志,发现最近没有登 ...
- 使用Java实现二叉树的添加,删除,获取以及遍历
一段来自百度百科的对二叉树的解释: 在计算机科学中,二叉树是每个结点最多有两个子树的树结构.通常子树被称作“左子树”(left subtree)和“右子树”(right subtree).二叉树常被用 ...
- 微信小程序保存图片到相册
先来看小程序中的保存图片到相册的api wx.saveImageToPhotosAlbum({ filePath : "./test.png", //这个只是测试路径,没有效果 s ...
- C#调用Python脚本打印pdf文件
介绍:通过pdf地址先将文件下载到本地,然后调用打印机打印,最后将下载的文件删除. 环境:windows系统.(windows64位) windows系统中安装python3.6.2环境 资料: O ...
- TIMO后台管理系统-基于SpringBoot开发
项目介绍 TIMO后台管理系统,基于SpringBoot2.0 + Spring Data Jpa + Thymeleaf + Shiro 开发的后台管理系统,采用分模块的方式便于开发和维护,支持前后 ...
- Redis in .NET Core 入门:(3) Hash
第1篇:https://www.cnblogs.com/cgzl/p/10294175.html 第2篇 String:https://www.cnblogs.com/cgzl/p/10297565. ...