JVM處理未捕獲異常的方法詳解
前言
繼之前的文章詳解JVM如何處理異常,今天再次發(fā)布一篇比較關(guān)聯(lián)的文章,如題目可知,今天聊一聊在JVM中線程遇到未捕獲異常的問題,其中涉及到線程如何處理未捕獲異常和一些內(nèi)容介紹。
什么是未捕獲異常
未捕獲異常指的是我們在方法體中沒有使用try-catch捕獲的異常,比如下面的例子
private static void testUncaughtException(String arg) {
try {
System.out.println(1 / arg.length());
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
上面的代碼很有可能發(fā)生如下情況
- 如果方法參數(shù)arg傳遞null,會(huì)出現(xiàn)NullPointerException
- 如果參數(shù)arg傳遞內(nèi)容為空的字符串(“”),會(huì)出現(xiàn)ArithmeticException
對(duì)于上面的問題,我們不難發(fā)現(xiàn)
- 上面可能出現(xiàn)的NullPointerException和ArithmeticException都屬于Unchecked Exceptions
- 而ArithmeticException被我們?nèi)藶閠ry-catch捕獲了,它不符合本文對(duì)于未捕獲異常的定義
- NullPointerException 由于我們沒有catch住,就變成了我們要聊的未捕獲異常
- 另外,未捕獲異常實(shí)際是Unchecked Exceptions的子集
UncaughtExceptionHandler 是什么
- 它是線程遇到未捕獲異常的一個(gè)處理者接口
- 它包含一個(gè)方法
void uncaughtException(Thread t, Throwable e);用來處理接收處理異常發(fā)生后的操作,比如收集崩潰信息并上報(bào)等 - 可以通過 實(shí)例方法
Thread.setUncaughtExceptionHandler為某一個(gè)Thread實(shí)例設(shè)置未捕獲異常處理者 - 也可以通過 靜態(tài)方法
Thread.setDefaultUncaughtExceptionHandler設(shè)置所有Thread實(shí)例的未捕獲異常處理者
ThreadGroup 是什么
- ThreadGroup 是線程的集合
- ThreadGroup 也可以包含子ThreadGroup
- 除了初始的ThreadGroup 之外,每個(gè)ThreadGroup都有一個(gè)父 ThreadGroup
- ThreadGroup 自身實(shí)現(xiàn)了Thread.UncaughtExceptionHandler,用來相應(yīng)處理其內(nèi)部的線程和ThreadGroup發(fā)生未捕獲異常。
未捕獲異常處理者 設(shè)置指南

線程發(fā)生了未捕獲異常,JVM怎么處理
分發(fā)Throwable實(shí)例
當(dāng)線程A中出現(xiàn)了未捕獲異常時(shí),JVM會(huì)調(diào)用線程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);
}
獲取未捕獲異常處理者
每個(gè)線程會(huì)有一個(gè)變量(uncaughtExceptionHandler)來保存未捕獲異常的處理者
在線程需要確定Throwable分發(fā)目標(biāo)的處理者時(shí),優(yōu)先獲取當(dāng)前線程中uncaughtExceptionHandler變量
如果出問題線程的uncaughtExceptionHandler為null(即沒有顯式設(shè)置異常處理者),則使用自己所在的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分發(fā)給ThreadGroup
- ThreadGroup會(huì)嘗試轉(zhuǎn)給它的父ThreadGroup(如果存在的話)
- 如果上面沒有找到對(duì)應(yīng)的ThreadGroup,則嘗試獲取Thread.getDefaultUncaughtExceptionHandler()并分發(fā)
/**
* 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么?
這個(gè)問題,我們可以通過這樣一段代碼驗(yàn)證
private static void dumpThreadGroups() {
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
while(threadGroup != null) {
System.out.println("dumpThreadGroups threadGroup=" + threadGroup.getName());
threadGroup = threadGroup.getParent();
}
}
執(zhí)行該方法對(duì)應(yīng)的輸出是
dumpThreadGroups threadGroup=main
dumpThreadGroups threadGroup=system
因此我們可以發(fā)現(xiàn),初始的ThreadGroup是一個(gè)叫做system的ThreadGroup,而不是main ThreadGroup
setDefaultUncaughtExceptionHandler 設(shè)置的一定會(huì)被調(diào)用到么
這其實(shí)是一個(gè)很好的問題,答案是不一定會(huì)被調(diào)用,因?yàn)榭赡艽嬖谝韵碌那闆r
- 出問題的線程設(shè)置了對(duì)應(yīng)的UncaughtExcpetionHandler,優(yōu)先響應(yīng)分發(fā)到這個(gè)Handler
- 出問題的線程所在的ThreadGroup包括其祖先ThreadGroup 重寫了uncaughtException 也可能造成線程默認(rèn)的Handler無法被調(diào)用
- 出問題的線程重寫了dispatchUncaughtException 可能性較小
- 出問題的線程重寫了getUncaughtExceptionHandler 可能性較小
參考聲明
How uncaught exceptions are handled
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
IDEA引MAVEN項(xiàng)目jar包依賴導(dǎo)入問題解決方法
這篇文章主要介紹了IDEA引MAVEN項(xiàng)目jar包依賴導(dǎo)入問題解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
springboot微服務(wù)項(xiàng)目集成html頁面的實(shí)現(xiàn)
本文主要介紹了springboot微服務(wù)項(xiàng)目集成html頁面的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
如何解決創(chuàng)建maven工程時(shí),產(chǎn)生“找不到插件的錯(cuò)誤”問題
這篇文章主要介紹了如何解決創(chuàng)建maven工程時(shí),產(chǎn)生“找不到插件的錯(cuò)誤”問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Java對(duì)象布局(JOL)實(shí)現(xiàn)過程解析
這篇文章主要介紹了Java對(duì)象布局(JOL)實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
Springboot中Instant時(shí)間傳參及序列化詳解
這篇文章主要介紹了Springboot中Instant時(shí)間傳參及序列化詳解,Instant是Java8引入的一個(gè)精度極高的時(shí)間類型,可以精確到納秒,但實(shí)際使用的時(shí)候不需要這么高的精確度,通常到毫秒就可以了,需要的朋友可以參考下2023-11-11

