阅读 67

JVM this关键字和异常表的作用

先从段代码说起

关于异常和this的

  • 代码
public class ExceptionTest {

    public void test(){
       try{
           InputStream inputStream = new FileInputStream("test.cpp");
           ServerSocket serverSocket = new ServerSocket(3306);
           serverSocket.accept();
       }catch (FileNotFoundException notFound){

       }catch (IOException ioException){

       }catch (Exception exception){

       }finally{
           System.out.println("finally");
       }
    }
}
复制代码
  • 反编译

image.png

在Java代码层面没有参数,但是在字节码层面是有参数的。

隐藏的实例方法参数就是 this

  • 对于Java类中的每一个实例方法(非static方法),其在编译后所生成的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this),它位于方法的第一个参数位置处;这样,我们就可以在Java的实例方法中使用this去访问当前对象的属性以及其他方法。
  • 这个操作是在编译期间完成的,即由javac编译器在编译的时候将对this的访问转化为对一个普通实例方法参数的访问,接下来在运行期间,由JVM在调用实例方法时,自动向实例方法传入该this参数。所以,在实例方法的局部变量表中,至少会有一个指向当前对象的局部变量。

再解释max_locals(最大局部变量)

  • 该代码max_locals为什么是 4?

image.png

  • 代码的try中有两个显式定义的局部变量,这就有2个了。
  • 再加上隐藏的this,这一共就有3个了。
  • 最后,留一个给Exception局部变量,虽然代码中有多个catch,但是,如果有异常的话只有一个catch会被执行。可以理解为这个Exception局部变量会在运行期间等待着被传入catch(类似作为它的参数传入)。这样一共就有4个局部变量了。

异常表

在说异常表之前,先复习一下字节码Code属性的结构

  • attribute length 表示attribute所包含的字节数,不包含attribute_name_index和attribute_length字段。
  • max_stack 表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
  • max_locals 表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
  • code_length 表示该方法所包含的字节码的字节数以及具体的指令码。
  • 具体字节码即是该方法被调用时,虚拟机所执行的字节码。
  • exception_table 这里存放的是处理异常的信息。
  • 每个 exception_table 表项由 start_pcend_pchandler_pccatch_type 组成。

  • start_pcend_pc 表示在 code 数组中的从 start_pcend_pc 处(包含start_pc,不包含end pc)的指令抛出的异常会由这个表项来处理。
  • handler_pc 表示处理异常的代码的开始处catch_type 表示会被处理的异常类型,它指向常量池里的一个异常类。当 catch_type为0时,表示处理所有的异常

阅读字段表

image.png

上面的异常表明明有一个Exception最顶层的异常,为啥还多出一个any来表示捕获所有异常
只能说代码层面Exception是最顶层的,但是在字节码层面就不一样了。any表示哪些连Exception都无法处理的异常。

再者,如果在方法签名上throws异常

  • 代码
public void test() throws NullPointerException,FileNotFoundException,IOException{
   try{
       InputStream inputStream = new FileInputStream("test.cpp");
       ServerSocket serverSocket = new ServerSocket(3306);
       serverSocket.accept();
   }catch (FileNotFoundException notFound){

   }catch (IOException ioException){

   }catch (Exception exception){

   }finally{
       System.out.println("finally");
   }
}
复制代码
  • 多了点东西

image.png

总之,Java字节码对于异常的处理方式:

  • 统一采用异常表的方式来对异常进行处理。
  • 在jdk 1.4.2之前的版本中,并不是使用异常表的方式来对异常进行处理的,而是采用特定的指令方式。
  • 当异常处理存在finally语句块时,现代化的JVM采取的处理方式是将finally语句块的字节码拼接到每一个catch块后面。
  • 换句话说,程序中存在多少个catch块,就会在每一个catch块后面重复多少个finally语句块的字节码。
文章分类
后端
文章标签