Jvm 学习——异常处理

异常表

每一个 try语句块 catch的异常都与异常表中的一项相对应,异常表中的每一项都包括:

  1. 起点
  2. 终点,始终把 catch异常位置的 pc指针偏移量的最大值大1
  3.   处理异常时跳转到的字节码序列中的 pc指针偏移量
  4.   被 catch的异常类的常量池索引

 

例如:

public   class  Test  {
    
public   static   void  main(String[] args)  {

        
try   {
            Class.forName(
" java.lang.String " );
        }
  catch  (ClassNotFoundException e)  {
            e.printStackTrace();
        }


    }

}

javap –c 查看字节码如下:

 

Compiled from  " Test.java "
public   class  Test  extends  java.lang.Object {
public  Test();
  Code:
   
0 :    aload_0
   
1 :    invokespecial    # 1 // Method java/lang/Object."<init>":()V
    4 :     return

public   static   void  main(java.lang.String[]);
  Code:
   
0 :    ldc    # 2 // String java.lang.String
    2 :    invokestatic    # 3 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
    5 :    pop    
   
6 :     goto      14
   
9 :    astore_1
   
10 :    aload_1
   
11 :    invokevirtual    # 5 // Method java/lang/ClassNotFoundException.printStackTrace:()V
    14 :     return
  Exception table:
   from   to  target type
     
0       6       9    Class java / lang / ClassNotFoundException
}

 

可见 ClassNotFoundException异常可能会在 0~6之间抛出, 9开始处的代码处理此异常。

 

当产生异常的时候, jvm将会在整个异常表中搜索与之匹配的项,如果当前 pc在异常表入口所指的范围内,并且所抛出的异常是此入口所指向的类或者其子类,则跳转到对应的处理代码继续执行。

 

方法可能会抛出哪些已检查异常

Class文件的 attribute_info中保存有 Exceptions属性,记录着每个方法 throws的异常信息。具体的可以查看 class类文件格式相关的文章。

 

athrow指令从栈顶弹出 Throwable对象引用,抛出异常。

 

finally语句

在 jvm规范中, finally语句是通过 jsr/jsr_w与 ret指令实现的。当执行 jsr/jsr_w的时候将 finally执行完成后的返回地址压入栈中,进入 finally后会马上将此地址保存到一个局部变量中,执行完成后, ret从此局部变量中取出返回地址。???为什么会先把返回地址保存到局部变量中呢???因为,当从 finally语句返回的时候需要将返回地址成栈中弹出,当 finally语句非正常结束 (break,continue,return, 抛异常 )的时候就不用再考虑这个问题。

 

以下是 jvm 规范中 Compiling finally 的一段:

void  tryFinally()  {
    
try   {
        tryItOut();
    }
  finally   {
        wrapItUp();
    }

}

the compiled code is
Method 
void  tryFinally()
   
0      aload_0             //  Beginning of try block
    1     invokevirtual # 6           //  Method Example.tryItOut()V
    4      jsr  14              //  Call finally block
    7       return              //  End of try block
    8      astore_1             //  Beginning of handler for any throw
    9      jsr  14              //  Call finally block
   12      aload_1             //  Push thrown value
   13      athrow             //   and rethrow the value to the invoker
   14      astore_2             //  Beginning of finally block
   15      aload_0             //  Push this
   16      invokevirtual # 5           //  Method Example.wrapItUp()V
   19      ret  2              //  Return from finally block
Exception table:
       From     To     Target         Type
    
0          4          8            any

 

当 tryItOut排除任何异常后都将会被异常表中的 any项捕获,执行完 finally后,会执行 athrow指令将异常抛出。

 

从 jdk的某一个版本开始就不会编译出编译出含 jsr/jsr_w、 ret的字节码了,因为有指令上的缺陷,导致 jvm的检验和分析系统出现漏洞。

 

再说 finally的非正常退出

在 finally中使用 break、 continue、 return、抛出异常等认为是 finally的非正常结束。非正常结束的时候, ret指令不会被执行,很可能会出现意想不到的结果。如:

 

public   class  Test  {
    
public   static   boolean  test( boolean  b)  {
        
while  (b)  {
            
try   {
                
return   true ;
            }
  finally   {
                
/**/ /*
                break;                          始终返回false
                continue;                         javac编译再java执行会出现死循环
                                                在eclipse中甚至会出现报错:提示找到不main class
                return false;                     始终返回false
                throw new RuntimeException("");    抛出异常
                 
*/

            }

        }


        
return   false ;
    }


    
public   static   void  main(String[] args)  {
        System.out.println(test(
true ));
    }

}


建议:在写 finally 语句的时候,尽量避免非正常结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值