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

业余草

共 3527字,需浏览 8分钟

 ·

2022-09-17 16:23

你知道的越多,不知道的就越多,业余的像一棵小草!

你来,我们一起精进!你不来,我和你的竞争对手一起精进!

编辑:业余草

juejin.cn/post/7003717101537984525

推荐:https://www.xttblog.com/?p=5357

先从段代码说起

我历史文章中已经写过关于 this 这个关键字的秘密了(《阿里面试题:Java中this和super关键字的底层实现原理》),不知道还有多少人记得。今天我们继续聊聊 this 的底层实现以及 Java 中 Exception 的底层实现。

关于异常和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");
       }
    }
}

反编译截图如下:

this关键字反编译

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

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

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

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

  • 该代码 max_locals 为什么是 4?
最大局部变量
  • 代码的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_pc ,**end_pc「,」handler_pc「,」catch_type** 组成。
  • start_pcend_pc 表示在 code 数组中的从 start_pcend_pc 处(「包含start_pc,不包含end pc「)的」指令」抛出的异常会由这个表项来处理。

  • handler_pc 表示「处理异常的代码的开始处」。**catch_type** 表示会被处理的异常类型,它「指向」常量池里的一个异常类。当 catch_type为0时,表示处理所有的异常」

阅读字段表

阅读字段表

「上面的异常表明明有一个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");
   }
}
  • 多了点东西
异常表

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

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

参考资料

浏览 25
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报