JVM如何保证finally块的执行?

 小女人hoffix_523 发布于 2023-02-06 10:17

这个问题针对的是JVM如何能够保证finally块的执行(假设JVM没有崩溃并且线程没有被中断或退出).

在面试问题的提示下,我试图了解JVM如何确保即使在奇怪的情况下也能执行finally块...请考虑以下代码:

try{

    int[] someArray = new int[10];
    int invalid = someArray[10];
}
catch(IndexOutOfBoundsException e){

    throw new RuntimeException("Other Exception");
}
finally{

    //close open files or HTTP connections etc.
}


虽然这可能是一个奇怪的情况,但仍然保证执行finally块,尽管没有明确处理其他异常.JVM如何处理这样的情况?

我的想法:

根据我的理解并且到目前为止已经读过,当遇到未处理的异常时,控制从当前线程转移到ThreadGroup我认为的那个线程.是否有一些条款ThreadGroup可以检查最终需要执行的块?我能想到的唯一另一件事可能是finally块的地址存储在某个地方.然后JVM在检测到异常时执行goto,并在finally块执行完毕后返回异常.

谁能澄清这个过程究竟是如何发生的?

2 个回答
  • 编译这个小程序(我意识到我应该使用你的例子,但它没有区别)

    public static void main(String[] args) {
        try {
            Float s = Float.parseFloat("0.0327f");
        } finally {
            System.out.println("hello");
        }
    }
    

    我用了

    >java -version 
    java version "1.8.0-ea"  // should be same for 7
    Java(TM) SE Runtime Environment (build 1.8.0-ea-b118)
    Java HotSpot(TM) 64-Bit Server VM (build 25.0-b60, mixed mode)
    

    然后执行

    javac -v -c <fully qualified class name>
    

    获取字节码.你会看到类似的东西

    public static void main(java.lang.String[]);
      flags: ACC_PUBLIC, ACC_STATIC
      Code:
        stack=2, locals=3, args_size=1
           0: ldc           #2                  // String 0.0327f
           2: invokestatic  #3                  // Method java/lang/Float.parseFloat:(Ljava/lang/String;)F
           5: invokestatic  #4                  // Method java/lang/Float.valueOf:(F)Ljava/lang/Float;
           8: astore_1
           9: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          12: ldc           #6                  // String hello
          14: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          17: goto          31
          20: astore_2
          21: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          24: ldc           #6                  // String hello
          26: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          29: aload_2
          30: athrow
          31: return
        Exception table:
           from    to  target type
               0     9    20   any
              20    21    20   any
        LineNumberTable:
          line 10: 0
          line 12: 9
          line 13: 17
          line 12: 20
          line 14: 31
        StackMapTable: number_of_entries = 2
             frame_type = 84 /* same_locals_1_stack_item */
            stack = [ class java/lang/Throwable ]
             frame_type = 10 /* same */
    

    你会注意到里面finally代码出现两次,一次出现在前面goto,一次出现之后.您还会注意到Exception table,如果在某行发生异常,则指定哪个语句.

    因此,如果任何异常声明0-9之间的情况,请转到第20行并执行内部的一切finally,之后的goto.如果没有异常发生,执行finally,然后执行goto跳过finally后的goto.

    在所有情况下,您都将执行finally块内的代码.

    其他异常没有明确处理

    使用finally块,Exception table将创建一个将处理任何类型的条目Throwable.


    这是字节码指令的列表.

    2023-02-06 10:19 回答
  • 我相信这个博客清楚地描述了内部:

    如果方法定义了try-catch或try-finally异常处理程序,那么将创建一个Exception Table.它包含每个异常处理程序或finally块的信息,包括处理程序应用的范围,正在处理的异常类型以及处理程序代码的位置.

    抛出异常时,JVM在当前方法中查找匹配的处理程序,如果没有找到,则方法突然弹出当前堆栈帧,并在调用方法(新的当前帧)中重新抛出异常.如果在弹出所有帧之前未找到异常处理程序,则终止该线程.如果在最后一个非守护程序线程中抛出异常,这也可能导致JVM本身终止,例如,如果该线程是主线程.

    最后,异常处理程序匹配所有类型的异常,因此每当抛出异常时总是执行.在没有抛出异常的情况下,仍然在方法结束时执行finally块,这是通过在执行return语句之前立即跳转到finally处理程序代码来实现的.

    2023-02-06 10:21 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有