Shun 发表于 2021-10-6 10:56:10

JVM处理未捕获异常的方法详解

这篇文章主要给大家介绍了关于JVM处理未捕获异常的相关资料,文中通过示例代码以及图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
前言
继之前的文章详解jvm如何处理异常,今天再次发布一篇比较关联的文章,如题目可知,今天聊一聊在jvm中线程遇到未捕获异常的问题,其中涉及到线程如何处理未捕获异常和一些内容介绍。
什么是未捕获异常
未捕获异常指的是我们在方法体中没有使用try-catch捕获的异常,比如下面的例子


private static void testuncaughtexception(string arg) {
try {
system.out.println(1 / arg.length());
} catch (arithmeticexception e) {
e.printstacktrace();
}
}
上面的代码很有可能发生如下情况

[*]如果方法参数arg传递null,会出现nullpointerexception
[*]如果参数arg传递内容为空的字符串(“”),会出现arithmeticexception
对于上面的问题,我们不难发现

[*]上面可能出现的nullpointerexception和arithmeticexception都属于unchecked exceptions
[*]而arithmeticexception被我们人为try-catch捕获了,它不符合本文对于未捕获异常的定义
[*]nullpointerexception 由于我们没有catch住,就变成了我们要聊的未捕获异常
[*]另外,未捕获异常实际是unchecked exceptions的子集
uncaughtexceptionhandler 是什么

[*]它是线程遇到未捕获异常的一个处理者接口
[*]它包含一个方法void uncaughtexception(thread t, throwable e); 用来处理接收处理异常发生后的操作,比如收集崩溃信息并上报等
[*]可以通过 实例方法 thread.setuncaughtexceptionhandler 为某一个thread实例设置未捕获异常处理者
[*]也可以通过 静态方法 thread.setdefaultuncaughtexceptionhandler 设置所有thread实例的未捕获异常处理者
threadgroup 是什么

[*]threadgroup 是线程的集合
[*]threadgroup 也可以包含子threadgroup
[*]除了初始的threadgroup 之外,每个threadgroup都有一个父 threadgroup
[*]threadgroup 自身实现了thread.uncaughtexceptionhandler,用来相应处理其内部的线程和threadgroup发生未捕获异常。
未捕获异常处理者 设置指南

线程发生了未捕获异常,jvm怎么处理
分发throwable实例
当线程a中出现了未捕获异常时,jvm会调用线程a的dispatchuncaughtexception(throwable)方法


/**
* dispatch an uncaught exception to the handler. this method is
* intended to be called only by the jvm.
*/
private void dispatchuncaughtexception(throwable e) {
getuncaughtexceptionhandler().uncaughtexception(this, e);
}
获取未捕获异常处理者
每个线程会有一个变量(uncaughtexceptionhandler)来保存未捕获异常的处理者
在线程需要确定throwable分发目标的处理者时,优先获取当前线程中uncaughtexceptionhandler变量
如果出问题线程的uncaughtexceptionhandler为null(即没有显式设置异常处理者),则使用自己所在的threadgroup来作为未捕获异常处理者。


/**
* returns the handler invoked when this thread abruptly terminates
* due to an uncaught exception. if this thread has not had an
* uncaught exception handler explicitly set then this thread's
* <tt>threadgroup</tt> object is returned, unless this thread
* has terminated, in which case <tt>null</tt> is returned.
* @since 1.5
* @return the uncaught exception handler for this thread
*/
public uncaughtexceptionhandler getuncaughtexceptionhandler() {
return uncaughtexceptionhandler != null ?
uncaughtexceptionhandler : group;
}
如果throwable分发给threadgroup

[*]threadgroup会尝试转给它的父threadgroup(如果存在的话)
[*]如果上面没有找到对应的threadgroup,则尝试获取thread.getdefaultuncaughtexceptionhandler()并分发


/**
* called by the java virtual machine when a thread in this
* thread group stops because of an uncaught exception, and the thread
* does not have a specific {@link thread.uncaughtexceptionhandler}
* installed.
* <p>
* the <code>uncaughtexception</code> method of
* <code>threadgroup</code> does the following:
* <ul>
* <li>if this thread group has a parent thread group, the
* <code>uncaughtexception</code> method of that parent is called
* with the same two arguments.
* <li>otherwise, this method checks to see if there is a
* {@linkplain thread#getdefaultuncaughtexceptionhandler default
* uncaught exception handler} installed, and if so, its
* <code>uncaughtexception</code> method is called with the same
* two arguments.
* <li>otherwise, this method determines if the <code>throwable</code>
* argument is an instance of {@link threaddeath}. if so, nothing
* special is done. otherwise, a message containing the
* thread's name, as returned from the thread's {@link
* thread#getname getname} method, and a stack backtrace,
* using the <code>throwable</code>'s {@link
* throwable#printstacktrace printstacktrace} method, is
* printed to the {@linkplain system#err standard error stream}.
* </ul>
* <p>
* applications can override this method in subclasses of
* <code>threadgroup</code> to provide alternative handling of
* uncaught exceptions.
*
* @param t the thread that is about to exit.
* @param e the uncaught exception.
* @since jdk1.0
*/
public void uncaughtexception(thread t, throwable e) {
if (parent != null) {
parent.uncaughtexception(t, e);
} else {
thread.uncaughtexceptionhandler ueh =
thread.getdefaultuncaughtexceptionhandler();
if (ueh != null) {
ueh.uncaughtexception(t, e);
} else if (!(e instanceof threaddeath)) {
system.err.print("exception in thread \""
   + t.getname() + "\" ");
e.printstacktrace(system.err);
}
}
}
将上面的处理流程做成图的形式,就是下图所示

注:上述图片来自https://www.javamex.com/tutorials/exceptions/exceptions_uncaught_handler.shtml
questions
初始的threadgroup是什么
上面提到了初始的threadgroup没有父threadgroup,是主线程所在的threadgroup么?
这个问题,我们可以通过这样一段代码验证


private static void dumpthreadgroups() {
threadgroup threadgroup = thread.currentthread().getthreadgroup();
while(threadgroup != null) {
system.out.println("dumpthreadgroups threadgroup=" + threadgroup.getname());
threadgroup = threadgroup.getparent();
}
}
执行该方法对应的输出是

dumpthreadgroups threadgroup=main
dumpthreadgroups threadgroup=system
因此我们可以发现,初始的threadgroup是一个叫做system的threadgroup,而不是main threadgroup
setdefaultuncaughtexceptionhandler 设置的一定会被调用到么
这其实是一个很好的问题,答案是不一定会被调用,因为可能存在以下的情况

[*]出问题的线程设置了对应的uncaughtexcpetionhandler,优先响应分发到这个handler
[*]出问题的线程所在的threadgroup包括其祖先threadgroup 重写了uncaughtexception 也可能造成线程默认的handler无法被调用
[*]出问题的线程重写了dispatchuncaughtexception 可能性较小
[*]出问题的线程重写了getuncaughtexceptionhandler 可能性较小
参考声明
how uncaught exceptions are handled
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对CodeAE代码之家的支持。
原文链接:https://droidyue.com/blog/2019/01/06/how-java-handle-uncaught-exceptions/

http://www.zzvips.com/article/174026.html
页: [1]
查看完整版本: JVM处理未捕获异常的方法详解