【问题标题】:how the Try catch finally block is executed by JVMJVM 如何执行 Try catch finally 块
【发布时间】:2013-10-23 10:20:44
【问题描述】:

根据 Java 语言规范,Section §14.20.2

带有 finally 块的 try 语句通过首先执行 try 块来执行。然后有一个选择:

  • 如果 try 块的执行正常完成,那么 finally 块被执行,然后有一个选择:
    • 如果 finally 块正常完成,则 try 语句正常完成。
    • 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成

如果我正确解释它,那么在执行 try 块之后最终会被调用,但是这一切是如何工作的以及为什么我得到输出,

public static int TestTryFinallyBlock()  
{
    int  i =0;
    try 
    {
        i= 10; //Perform some more operation
        return i;
    }       
    finally
    {
        i = 40; 
    }
}

public static void main( String[] args )
{
    int i1 = TestTryFinallyBlock(); //Here the output was 10 not 40
}   

我想知道这个东西是如何产生输出 10 的。

是不是在执行try块遇到return语句时输出值已经压栈,然后执行finally块

我知道先遇到 return 然后 finally 块运行所以输出是 10,但是 jvm 如何解释或 jvm 如何处理或转换 try finally 块?
是jvm使用GOTO段跳转段到finally段还是栈已经维护?

【问题讨论】:

  • try/catch/最终是由编译器执行还是由jvm执行?
  • @vikingsteve 谢谢我编辑了我的问题

标签: java try-finally


【解决方案1】:

经过一番搜索并查看生成了哪些字节码,我发现实际上并没有看起来的 finally 块,也没有 JVM 生成的 jump 或 goto 语句。
上面的代码被翻译成(如果我正确解释字节码,如果我错了请纠正我)

public static int TestTryFinallyBlock()  
{
  int returnValue; //A temporary return variable
  try
  {
     int  i = 0;     
     i = 10; 
     returnValue = i; 
     i = 40; 
     return returnValue;    
  }
  catch (RuntimeException e)
  {
       i = 40; //finally section code id copied here too
       throw e;
  }
}

注意事项:如果 'i' 是对可变类对象的引用,并且对象的内容在 finally 块中发生了更改,那么这些更改将反映在也有返回值。

【讨论】:

    【解决方案2】:

    最后编译

    try-finally 语句的编译类似于 try-catch。在将控制转移到 try 语句之外之前,无论该转移是正常的还是突然的,因为抛出了异常,都必须首先执行 finally 子句。对于这个简单的例子:

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

    编译后的代码是:

    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 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
    

    有四种方法可以让控制在 try 语句之外传递:通过该块的底部、返回、执行 break 或 continue 语句或引发异常。

    要了解更多关于 javac 如何解释 finally 块。请参考 JLS - 3.13. Compiling finally

    【讨论】:

    • 我想知道栈是如何维护的或者编译器是如何转换try finally块的?这只是我的例子,我不需要输出 40
    • 这仅对版本号为 50.0 或以下的类文件有效。 3.13. Compiling finally
    【解决方案3】:

    当你输入return时,方法已经准备好返回10。10作为返回值在栈上。 finally 块被执行并将i 设置为40——但i 与返回值的位置不同。现在,如果有这样的副作用:

    public static int TestTryFinallyBlock()  
    {
        int  i =0;
        try 
        {
            i= 10; //Perform some more operation
            return i;
        }       
        finally
        {
            i = 40; 
            System.out.println("local: "+i);
        }
    }
    

    40 打印出来。

    【讨论】:

    • 我想知道栈是如何维护的或者try finally块是如何被编译器转换的?
    • @dbw:我建议你编译你的代码,然后用javap -c 反汇编它来查看各个字节码。阅读本文并手头有一份Java Virtual Machine Specification(尤其是关于framesJava Virtual Machine Instruction Set 结构的部分)以了解确切发生了什么。
    • @hexafraction 你的链接确实帮助我找到了我正在寻找的东西
    【解决方案4】:

    这是因为 return 语句的函数方式以及它与 try with finally 语句的交互方式。

    JLS 的Section §14.17 描述了return 语句。

    带有 Expression 的 return 语句试图将控制权转移给包含它的方法的调用者;表达式的值成为方法调用的值。更准确地说,执行这样的 return 语句首先评估 Expression。如果 Expression 的计算由于某种原因突然完成,那么 return 语句会因为这个原因而突然完成。如果表达式的评估正常完成,产生一个值 V,那么 return 语句会突然完成,原因是返回值 V。

    最后一句表示如果return语句的表达式正常求值,那么return语句abrutptly完成。在您的示例中,try 块由于 return 语句而突然终止,原因是 return 值 10(i评估为值 10)。

    由于在您的示例中,return 正在尝试使用 finally 块,JLS 的Section §14.20.2 告诉我们接下来会发生什么:

    • 如果 try 块的执行由于任何其他原因突然完成 R,然后执行finally块,然后有一个选择:
      • 如果 finally 块正常完成,则由于原因 R,try 语句会突然完成。
      • 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成(并且原因 R 被丢弃)。

    所以,由于 try 块突然终止,因为 return 语句被 求值 为值 10,并且因为 finally 块正常完成,所以该方法返回 10。

    【讨论】:

      【解决方案5】:

      现在 i 的值是 40,但你没有得到 40,因为你得到了 (returned) 之前的值 40强>。

      好像

      i=10
      return i
      i=40
      if return here,you get 40
      

      虽然不是一个好习惯,但只是为了演示。

      public static int TestTryFinallyBlock()  
          {
              int  i =0;
              try 
              {
                  i= 10; //Perform some more operation
      
              }       
              finally
              {
                  i = 40;
                  return i;
              }
          }
      

      现在 40 的值将包含在 i 中。

      *附带说明:*除了资源清理之外,切勿编写任何业务逻辑。这会降低可读性并导致错误。

      【讨论】:

      • 我想知道栈是如何维护的或者编译器是如何转换try finally块的?这只是示例
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      • 2015-09-05
      • 2012-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多