Jvm 学习——异常处理

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_wret指令实现的。当执行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_wret的字节码了,因为有指令上的缺陷,导致jvm的检验和分析系统出现漏洞。

 

再说finally的非正常退出

finally中使用breakcontinuereturn、抛出异常等认为是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语句的时候,尽量避免非正常结束!


 

你可能感兴趣的:(Jvm 学习——异常处理)