LetsCoding.cn

          天地之間有桿秤,拿秤砣砸老百姓。

          JVM中的異常處理

          歡迎來到“Under The Hood”第六期。本期我們介紹JVM處理異常的方式,包括如何拋出和捕獲異常及相關(guān)的字節(jié)碼指令。但本文不會(huì)討論finally子句,這是下期的主題。你可能需要閱讀往期的文章才能更好的理解本文。

          異常處理

          在程序運(yùn)行時(shí),異常讓你可以平滑的處理意外狀況。為了演示JVM處理異常的方式,考慮NitPickyMath類,它提供對(duì)整數(shù)進(jìn)行加,減,乘,除以及取余的操作。

          NitPickyMath提供的這些操作和Java語言的“+”,“-”,“*”,“/”和“%”是一樣的,除了NitPickyMath中的方法在以下情況下會(huì)拋出檢查型(checked)異常:上溢出,下溢出以及被0除。0做除數(shù)時(shí),JVM會(huì)拋出ArithmeticException異常,但是上溢出和下溢出不會(huì)引發(fā)任何異常。NitPickyMath中拋出異常的方法定義如下:

          1. class OverflowException extends Exception {
          2. }
          3. class UnderflowException extends Exception {
          4. }
          5. class DivideByZeroException extends Exception {
          6. }

          NitPickyMath類中的remainder()方法就是一個(gè)拋出和捕獲異常的簡單方法。

          1. static int remainder(int dividend, int divisor)
          2.     throws DivideByZeroException {
          3.     try {
          4.         return dividend % divisor;
          5.     }
          6.     catch (ArithmeticException e) {
          7.         throw new DivideByZeroException();
          8.     }
          9. }

          remainder()方法,只是簡單的對(duì)當(dāng)作參數(shù)傳遞進(jìn)來的2個(gè)整數(shù)進(jìn)行取余操作。如果取余操作的除數(shù)是0,會(huì)引發(fā)ArithmeticException異常。remainder()方法捕獲這個(gè)異常,并重新拋出DivideByZeroException異常。

          DivideByZeroException和ArithmeticException的區(qū)別是,DivideByZeroException是檢查型(checked)異常,而ArithmeticException是非檢查(unchecked)型異常。由于ArithmeticException是非檢查型異常,一個(gè)方法就算會(huì)拋出該異常,也不必在其throw子句中聲明它。任何Error或RuntimeException異常的子類異常都是非檢查型異常。(ArithmeticException就是RuntimeException的子類。)通過捕獲ArithmeticException和拋出DivideByZeroException,remainder()方法強(qiáng)迫它的調(diào)用者去處理除數(shù)為0的可能性,要么捕獲它,要么在其throw子句中聲明DivideByZeroException異常。這是因?yàn)椋馜ivideByZeroException這種在方法中拋出的檢查型異常,要么在方法中捕獲,要么在其throw子句中聲明,二者必選其一。而像ArithmeticException這種非檢查型異常,就不需要去顯式捕獲和聲明。

          javac為remainder()方法生成的字節(jié)碼序列如下:

          1. // The main bytecode sequence for remainder:
          2. 0 iload_0               // Push local variable 0 (arg passed as divisor)
          3. 1 iload_1               // Push local variable 1 (arg passed as dividend)
          4. 2 irem                  // Pop divisor, pop dividend, push remainder
          5. 3 ireturn               // Return int on top of stack (the remainder)
          6. // The bytecode sequence for the catch (ArithmeticException) clause:
          7. 4 pop                   // Pop the reference to the ArithmeticException
          8.                         // because it is not used by this catch clause.
          9. 5 new #5 < Class DivideByZeroException >
          10.                         // Create and push reference to new object of class
          11.                         // DivideByZeroException.
          12. 8 dup                   // Duplicate the reference to the new
          13.                         // object on the top of the stack because it
          14.                         // must be both initialized
          15.                         // and thrown. The initialization will consume
          16.                         // the copy of the reference created by the dup.
          17. 9 invokenonvirtual #9 < Method DivideByZeroException.< init >()V >
          18.                         // Call the constructor for the DivideByZeroException
          19.                         // to initialize it. This instruction
          20.                         // will pop the top reference to the object.
          21. 12 athrow               // Pop the reference to a Throwable object, in this
          22.                         // case the DivideByZeroException,
          23.                         // and throw the exception.

          remainder()方法的字節(jié)碼有2個(gè)單獨(dú)的部分。第一部分是該方法的正常執(zhí)行路徑,這部分從第0行開始,到第3行結(jié)束。第二部分是從第4行開始,到12行結(jié)束的catch子句。

          主字節(jié)碼序列中的irem指令可能會(huì)拋出ArithmeticException異常。如果異常發(fā)生了,JVM通過在異常表中查找匹配的異常,它會(huì)知道要跳轉(zhuǎn)到相應(yīng)的異常處理的catch子句的字節(jié)碼序列部分。每個(gè)捕獲異常的方法,都跟類文件中與方法字節(jié)碼一起交付的異常表關(guān)聯(lián)。每一個(gè)捕獲異常的try塊,都是異常表中的一行。每行4條信息:開始行號(hào)(from)和結(jié)束行號(hào)(to),要跳轉(zhuǎn)的字節(jié)碼序列行號(hào)(target),被捕獲的異常類的常量池索引(type)。remainder()方法的異常表如下所示:

          FROM
          TO
          TARGET
          TYPE
          0 4 4 < Class java.lang.ArithmeticException >

          上面的異常表表明,行號(hào)1到3范圍內(nèi),ArithmeticException將被捕獲。異常表中的“to”下面的結(jié)束行號(hào)始終比異常捕獲的最大行號(hào)大1,上表中,結(jié)束行號(hào)為4,而異常捕獲的最大行號(hào)是3。行號(hào)0到3的字節(jié)碼序列對(duì)應(yīng)remainder()方法中的try塊。“target”列中,是行0到3的字節(jié)碼發(fā)生ArithmeticException異常時(shí)要跳轉(zhuǎn)到的目標(biāo)行號(hào)。

          如果方法執(zhí)行過程中產(chǎn)生了異常,JVM會(huì)在異常表中查找匹配行。異常表中的匹配行要符合下面的條件:當(dāng)前pc寄存器的值要在該行的表示范圍之內(nèi),[from, to),且產(chǎn)生的異常是該行所指定的異常類或其子類。JVM按從上到下的次序查找異常表。當(dāng)找到了第一個(gè)匹配行,JVM把pc寄存器設(shè)為新的跳轉(zhuǎn)行號(hào),從此行繼續(xù)往下執(zhí)行。如果找不到匹配行,JVM彈出當(dāng)前棧幀,并重新拋出同一個(gè)異常。當(dāng)JVM彈出當(dāng)前棧幀時(shí),它會(huì)終止當(dāng)前方法的執(zhí)行,返回到調(diào)用該方法的上一個(gè)方法那里。這時(shí),在上一個(gè)方法里,并不會(huì)繼續(xù)正常的執(zhí)行過程,而是拋出同樣的異常,促使JVM重新查找該方法的異常表。

          Java程序員可以用throw語句拋出像remainder()方法的catch子句中的異常,DivideByZeroException。下表列出了拋出異常的字節(jié)碼:

          OPCODE
          OPERAND(S)
          DESCRIPTION
          athrow (none) pops Throwable object reference, throws the exception

          athrow指令把棧頂元素彈出,該元素必須是Throwable的子類或其自身的對(duì)象引用,而拋出的異常類型由棧頂彈出的對(duì)象引用所指明。

          本文譯自:How the Java virtual machine handles exceptions

          本文出自:碼農(nóng)合作社 》JVM中的異常處理,轉(zhuǎn)載請(qǐng)注明。

          posted on 2014-06-02 03:54 Rolandz 閱讀(2773) 評(píng)論(0)  編輯  收藏 所屬分類: 編程實(shí)踐

          導(dǎo)航

          統(tǒng)計(jì)

          留言簿(1)

          隨筆分類(12)

          隨筆檔案(19)

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 长兴县| 榆中县| 玉田县| 肇州县| 文安县| 桦甸市| 富蕴县| 含山县| 花垣县| 合山市| 唐山市| 额尔古纳市| 曲阳县| 尚志市| 兴城市| 迁安市| 白朗县| 叶城县| 临城县| 衡南县| 上饶县| 东海县| 岚皋县| 鹤壁市| 晋江市| 皮山县| 鲁山县| 乌鲁木齐市| 广河县| 长葛市| 龙里县| 洪洞县| 蒙阴县| 凌海市| 上蔡县| 阜康市| 温宿县| 藁城市| 高尔夫| 漾濞| 敖汉旗|