廉頗老矣,尚能飯否

          java:從技術(shù)到管理

          常用鏈接

          統(tǒng)計(jì)

          最新評(píng)論

          JVM對(duì)Java異常的處理原理【轉(zhuǎn)載】

          最初我們用 Java 寫 JSP 的時(shí)候,幾乎可以不觸及異常,因?yàn)?Servlet 容器會(huì)把 API 拋出的異常包裝成 ServletException 丟給容器去處理。再后來應(yīng)用分層,代碼中要處理的異常便多了,一般會(huì)轉(zhuǎn)換成自定義的業(yè)務(wù)異常類,用 try-catch-throw customerException-finally。再到如今各種框架日臻成熟,代碼中顯式的異常處理又漸漸少了些,借助于 AOP 橫行,異常對(duì)業(yè)務(wù)的影響描述被移入到了配置文件中了,例如,事物處理、權(quán)限的控制等。

          這頗有些像手機(jī)的發(fā)展,當(dāng)通信技術(shù)不甚發(fā)達(dá)的時(shí)候,手里抓的是磚頭,信號(hào)是模擬的。后來慢慢瘦身成兩三根手指大小,甚至是就一支筆似的,可如今信息量大了,屏幕要大,再配上 QWERT 鍵盤,機(jī)身自然就肥碩了。

          當(dāng)然與手機(jī)的個(gè)頭變遷略有不同的是,任憑你怎么對(duì)待 Java 中異常,切入 AOP 也好,在 JVM 中處理異常的內(nèi)在機(jī)制始終未變。

          說到 Java 異常,無外乎就是 try、catch、finally、throw、throws 這么幾個(gè)關(guān)鍵字,這些個(gè)的用法是沒必要在這里講了。我們這里主要關(guān)鍵一下 catch 和 finally 是如何在編譯后的 class 字節(jié)碼中的。

          異常的拋出與捕獲,Catch 子句的表現(xiàn),來看看一段 Java 代碼及生成的相應(yīng)字節(jié)碼指令。

          1. package com.unmi;   
          2.   
          3. import java.io.UnsupportedEncodingException;   
          4.   
          5. public class AboutCatch {   
          6.        
          7.     public static void main(String[] args){   
          8.         try {   
          9.             transfer("JVM 對(duì) Java 異常的處理","gbk");   
          10.         } catch (Exception e) {   
          11.             //e.printStackTrace();   
          12.         }   
          13.     }   
          14.        
          15.     //字符集轉(zhuǎn)換的方法   
          16.     public static void transfer(String src, String charset)   
          17.             throws Exception{   
          18.         String result = "";   
          19.         try{   
          20.             //這行代碼可能會(huì)拋出空指針,不支持的字符集,數(shù)組越界的異常   
          21.             result = new String(src.getBytes(),0,10,charset);   
          22.         }catch(NullPointerException ne){   
          23.             System.out.println("捕獲到異常 ArithemticExcetipn");   
          24.             throw ne;   
          25.         }catch(UnsupportedEncodingException uee){   
          26.             System.out.println("捕獲到異常 UnsupportedEncodingException");   
          27.             throw uee;   
          28.         }catch(Exception ex){ //比如數(shù)組越界時(shí)在這里可捕獲到   
          29.             System.out.println("捕獲到異常 Exception");   
          30.             throw ex;   
          31.         }   
          32.         System.out.println(result);   
          33.     }   
          34. }   


          來看看上面代碼中的 transfer() 方法相應(yīng)的字節(jié)碼指令,編譯器是 Eclipse 3.3.2 的,它所用的 JDK 是 1.6.0_06,編譯兼容級(jí)別設(shè)置為 6.0。用命令 javap -c com.unmi.AboutCatch 在 Dos 窗口中就能輸出:

          public static void transfer(java.lang.String, java.lang.String)   throws java.lang.Exception;
            Code:
             0:   ldc     #30; //String
             2:   astore_2
             3:   new     #32; //class java/lang/String
             6:   dup
             7:   aload_0
             8:   invokevirtual   #34; //Method java/lang/String.getBytes:()[B
             11:  iconst_0
             12:  bipush  10
             14:  aload_1
             15:  invokespecial   #38; //Method java/lang/String."<init>":([BIILjava/lang/String;)V
             18:  astore_2
             19:  goto    55  //依據(jù)異常表執(zhí)行完異常處理塊后,再回到這里,然后 goto 到 55 號(hào)指令繼續(xù)執(zhí)行
             22:  astore_3
             23:  getstatic       #41; //Field java/lang/System.out:Ljava/io/PrintStream;
             26:  ldc     #47; //String 捕獲到異常 ArithemticExcetipn
             28:  invokevirtual   #49; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             31:  aload_3
             32:  athrow    //拋出 ArthemticException 異常
             33:  astore_3
             34:  getstatic       #41; //Field java/lang/System.out:Ljava/io/PrintStream;
             37:  ldc     #55; //String 捕獲到異常 UnsupportedEncodingException
             39:  invokevirtual   #49; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             42:  aload_3
             43:  athrow    //拋出 UnsupportedEncodingException 異常
             44:  astore_3
             45:  getstatic       #41; //Field java/lang/System.out:Ljava/io/PrintStream;
             48:  ldc     #57; //String 捕獲到異常 Exception
             50:  invokevirtual   #49; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             53:  aload_3
             54:  athrow   //拋出 Exception 異常
             55:  getstatic       #41; //Field java/lang/System.out:Ljava/io/PrintStream;
             58:  aload_2
             59:  invokevirtual   #49; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
             62:  return
            Exception table:  //這下面是一個(gè)異常表,所以異常不像普通代碼那樣是靠 goto 語句來跳轉(zhuǎn)的
             from   to  target type
               //0-19 號(hào)指令中,碰到 NullPointerException時(shí),跳到 22 號(hào)指令
               3    19    22   Class java/lang/NullPointerException

           

               //0-19 號(hào)指令中,碰到 UnsupportedEncodingException 時(shí),跳到 33 號(hào)指令 
               3    19    33   Class java/io/UnsupportedEncodingException

               //0-19 號(hào)指令中,碰到 NullPointerException時(shí),跳到 44 號(hào)指令
               3    19    44   Class java/lang/Exception

          說明:

          對(duì)于上面的程序,我們可以用下面代碼來調(diào)用看看輸出

          1) transfer("JVM 對(duì) Java 異常的處理","gbk");  //正常
          2) transfer(null, "gbk");                                         //空指針異常
          3) transfer("JVM 對(duì)","gbk");                               //數(shù)組越界異常
          4) transfer("JVM 對(duì)","gbk-1");                            //不支持的字符集異常

          最后可以把代碼中的
          catch(Exception ex){ //比如數(shù)組越界時(shí)在這里可捕獲到
             System.out.println("捕獲到異常 Exception");
             throw ex;
            }

          或是 main() 方法寫成

           public static void main(String[] args) throws Exception{
            transfer("JVM 對(duì) Java 異常的處理","gbk");
           }

          來試試,異常一直未得到處理對(duì) JVM 的影響

          字節(jié)碼中,紅色部分是我加上去的注釋,著重描了要關(guān)注的地方,其他的出入棧、方法調(diào)用的指令可不予以理會(huì),關(guān)鍵是只要知曉有一個(gè)異常表的存在,try 的范圍就是體現(xiàn)在異常表行記錄的起點(diǎn)和終點(diǎn)。JVM 在 try 住的代碼區(qū)間內(nèi)如有異常拋出的話,就會(huì)在當(dāng)前棧楨的異常表中,找到匹配類型的異常記錄的入口指令號(hào),然后跳到該指令處執(zhí)行。異常指令塊執(zhí)行完后,再回來繼續(xù)執(zhí)行后面的代碼。JVM 按照每個(gè)入口在表中出現(xiàn)的順序進(jìn)行檢索,如果沒有發(fā)現(xiàn)匹配的項(xiàng),JVM 將當(dāng)前棧幀從棧中彈出,再次拋出同樣的異常。當(dāng) JVM 彈出當(dāng)前棧幀時(shí),JVM 馬上終止當(dāng)前方法的執(zhí)行,并且返回到調(diào)用本方法的方法中,但是并非繼續(xù)正常執(zhí)行該方法,而是在該方法中拋出同樣的異常,這就使得 JVM 在該方法中再次執(zhí)行同樣的搜尋異常表的操作。

          上面那樣的內(nèi)層方法無法處理異常的層層向外拋,層層壓棧,這樣就形成一個(gè)異常棧。異常棧十分有利于我們透析問題之所在,例如 e.printStackTrace(); 或者帶參數(shù)的 e.printStackTrace(); 方法可將異常棧信息定向輸出到他處,還有 log4j 的 log.error(Throwable) 也有此功效。若是在行徑的哪層有能力處理該異常則已,否則直至 JVM,直接造成 JVM 崩潰掉。例如當(dāng) main() 方法也把異常拋了出去,JVM 此刻也就到了生命的盡頭。



          柳德才
          13691193654
          18942949207
          QQ:422157370
          liudecai_zan@126.com
          湖北-武漢-江夏-廟山

          posted on 2009-04-08 16:07 liudecai_zan@126.com 閱讀(896) 評(píng)論(0)  編輯  收藏 所屬分類: 程序人生

          主站蜘蛛池模板: 黎城县| 滁州市| 缙云县| 藁城市| 蒲城县| 上思县| 井陉县| 白城市| 金湖县| 凤台县| 灌南县| 庆元县| 达拉特旗| 曲阜市| 修文县| 茶陵县| 花垣县| 肇州县| 孟村| 宁津县| 科技| 潜江市| 弥勒县| 那坡县| 大宁县| 桂东县| 拉萨市| 唐海县| 临颍县| 安塞县| 商南县| 米林县| 互助| 蒙城县| 龙口市| 项城市| 沙洋县| 南阳市| 深泽县| 麻栗坡县| 莒南县|