Understanding try-catch-finally through bytecode

Scenes

For the following code:

public int test() {
        int x;
        try {
            x = 1;
            return x;
        } catch (Exception e) {
            x = 2;
            return x;
        } finally {
            x = 3;
        }
    }

in conclusion

  • If there is no exception belonging to Exception or its subclasses in the try statement, the return value is 1

  • If it appears, the return value is 2

  • If there is an exception other than Exception, there is no return, and the method exits abnormally

Explanation

The bytecode obtained through javap is as follows (main part):

public int test();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=5, args_size=1
         0: iconst_1 //将int类型常量1压入操作数栈-> 操作数栈:1,局部变量表:空
         1: istore_1 //将int类型值出栈,存入局部变量1-> 操作数栈:空,局部变量表:slot1=1,也就是x=1
         2: iload_1 //从局部变量表1中装载int类型值,压入操作数栈-> 操作数栈:1,局部变量表:slot1=1
         3: istore_2 //将int类型值出栈,存入局部变量2-> 操作数栈:空,局部变量表:slot1=1,slot2=1
         4: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot1=1,slot2=1
         5: istore_1 //将int类型值出栈,存入局部变量1-> 操作数栈:空,局部变量表:slot1=3,slot2=1
         6: iload_2 //从局部变量表2中装载int类型值,压入操作数栈-> 操作数栈:1,局部变量表:slot1=3,slot2=1
         7: ireturn //返回int类型,此时操作数栈顶为1,所以返回1(无异常情况)
         8: astore_2 //当0到3行出现异常跳至这里,将catch中的Exception e复制,存入局部变量表2->slot2 = e
         9: iconst_2 //将int类型常量2压入操作数栈-> 操作数栈:2,局部变量表:slot2=e
        10: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=2
        11: iload_1 //从局部变量表1中装载int类型值,压入操作数栈-> 操作数栈:2,局部变量表:slot1=2
        12: istore_3 //将int类型值出栈,存入局部变量表3-> 操作数栈:空,局部变量表:slot1=2,slot3=2
        13: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot1=2,slot3=2
        14: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=3,slot3=2
        15: iload_3 //从局部变量表3中装载int类型值,压入操作数栈-> 操作数栈:2,局部变量表:slot1=3,slot3=2
        16: ireturn //返回int类型,此时操作数栈顶为2,所以返回2(Exception 情况)
        17: astore        4 //不属于Exception及其子类的异常存入局部变量表4-> 局部变量表:slot4=异常引用
        19: iconst_3 //将int类型常量3压入操作数栈-> 操作数栈:3,局部变量表:slot4=异常引用
        20: istore_1 //将int类型值出栈,存入局部变量表1-> 操作数栈:空,局部变量表:slot1=3,slot4=异常引用
        21: aload         4 //将局部变量表4中的异常引用压入栈顶
        23: athrow //抛出栈顶异常
      Exception table:
         from    to  target type
             0     4     8   Class java/lang/Exception
             0     4    17   any
             8    13    17   any
            17    19    17   any

Comments are added to each line of bytecode in the above Code, and the state of the operand stack and local variable table is analyzed.

Among them, the bytecode line number 0 to 7 is the bytecode stream when there is no abnormality, and the return value is 1.

It should be noted that in the above Code, the 4th and 5th lines are the finally in: x=3. The compiler automatically generates the content of the finally statement block redundantly after each possible branch path to achieve finally semantics. The same goes for rows 13 and 14, and rows 19 and 20.

Then we look at the exception handling table:

Exception table:

         from    to  target type

             0     4     8   Class java/lang/Exception

             0     4    17   any

             8    13    17   any

            17    19    17   any

It means that in the bytecode line number:

  • If you find Exception or its subclasses from lines 0 to 3, skip to line 8 for processing; if you find other exceptions, skip to line 17 for processing

  • 8 to 12 lines, then skip to line 17 for processing

  • The fourth row in the exception table: 17 19 17 any (I don’t understand what it means here. I read the description of the exception table in the virtual machine specification. I only said to (that is, end_pc. The exclusivity is a historical error in the design. The same problem of from and target is to be supplemented after finding the relevant information. It may be that the virtual machine is specially processed in order to deal with a certain situation. If it is not possible, you still have to read the source code~)

Combining this exception table with the comments in Code, it can be found that if Exception and its subclass exceptions occur in the try statement, then the executed bytecode is lines 8-16, and the final return value is 2. For other exceptions, skip to line 17 for processing, execute lines 17-23, and finally throw the exception, and the method value is not returned.

Another problem can be found from the exception table. If an exception occurs in the catch block (lines 8 to 12), it will also jump to line 17 for processing, that is, execute the finally code block.

Note: For the statement x=1;, although two bytecodes of iconst and istore are reflected in the bytecode, they are still atomic operations. Atomic operations do not mean that there is only one instruction, but an uninterruptible one or A series of operations. You can compare the long and double non-atomic protocols.

Reference: "In-depth understanding of the Java virtual machine"

Guess you like

Origin blog.csdn.net/huangzhilin2015/article/details/114157287