JVM 如何处理未捕获异常

什么是未捕获异常

未捕获异常指的是我们在方法体中没有使用try-catch捕获的异常,比如下面的例子

1
2
3
4
5
6
7

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发生未捕获异常。

未捕获异常处理者 设置指南

how to set uncaught exception handler

线程发生了未捕获异常,JVM怎么处理

分发Throwable实例

当线程A中出现了未捕获异常时,JVM会调用线程A的dispatchUncaughtException(Throwable)方法

1
2
3
4
5
6
7

/**
* 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来作为未捕获异常处理者。

1
2
3
4
5
6
7
8
9
10
11
12
13

/**
* 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
* ThreadGroup object is returned, unless this thread
* has terminated, in which case null 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()并分发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

/**
* 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.
*

* The uncaughtException method of
* ThreadGroup does the following:
*

    *

  • If this thread group has a parent thread group, the
    * uncaughtException method of that parent is called
    * with the same two arguments.
    *
  • Otherwise, this method checks to see if there is a
    * {@linkplain Thread#getDefaultUncaughtExceptionHandler default
    * uncaught exception handler} installed, and if so, its
    * uncaughtException method is called with the same
    * two arguments.
    *
  • Otherwise, this method determines if the Throwable
    * 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 Throwable‘s {@link
    * Throwable#printStackTrace printStackTrace} method, is
    * printed to the {@linkplain System#err standard error stream}.
    *

*

* Applications can override this method in subclasses of
* ThreadGroup 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);
}
}
}

将上面的处理流程做成图的形式,就是下图所示

Uncaught exception process

注:上述图片来自https://www.javamex.com/tutorials/exceptions/exceptions_uncaught_handler.shtml

Questions

初始的ThreadGroup是什么

上面提到了初始的ThreadGroup没有父ThreadGroup,是主线程所在的ThreadGroup么?

这个问题,我们可以通过这样一段代码验证

1
2
3
4
5
6
7

private static void dumpThreadGroups() {
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
while(threadGroup != null) {
System.out.println(“dumpThreadGroups threadGroup=” + threadGroup.getName());
threadGroup = threadGroup.getParent();
}
}

执行该方法对应的输出是

1
2

dumpThreadGroups threadGroup=main
dumpThreadGroups threadGroup=system

因此我们可以发现,初始的ThreadGroup是一个叫做system的ThreadGroup,而不是main ThreadGroup

setDefaultUncaughtExceptionHandler 设置的一定会被调用到么

这其实是一个很好的问题,答案是不一定会被调用,因为可能存在以下的情况

  • 出问题的线程设置了对应的UncaughtExcpetionHandler,优先响应分发到这个Handler
  • 出问题的线程所在的ThreadGroup包括其祖先ThreadGroup 重写了uncaughtException 也可能造成线程默认的Handler无法被调用
  • 出问题的线程重写了dispatchUncaughtException 可能性较小
  • 出问题的线程重写了getUncaughtExceptionHandler 可能性较小

参考声明

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/20795,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?