好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

前言

继之前的文章 详解jvm如何处理异常 ,今天再次发布一篇比较关联的文章,如题目可知,今天聊一聊在 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发生未捕获异常。

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

线程发生了未捕获异常,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

  * <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()并分发

?

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.

  * <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么?

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

?

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();

  }

}

执行该方法对应的输出是

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

原文链接:https://droidyue.com/blog/2019/01/06/how-java-handle-uncaught-exceptions/

查看更多关于JVM处理未捕获异常的方法详解的详细内容...

  阅读:12次