From the JVM instruction level, try-catch-finally return value problem
It seems that many people are confused about the return value of the following method:
package cc.lixiaohui.demo; public class ReturnValueTest { public int test() { int a; try { a = 1; //int b = 1 / 0; return a; } catch (Exception e) { a = 2; return a; } finally { a = 3; } } }
The return value of the test method is naturally 1. If the commented line is removed, it will be 2.
Why?
View the bytecode with javap -verbose ReturnValueTest:
Focus on the test() method directive:
Compiled from "ReturnValueTest.java" public class cc.lixiaohui.demo.ReturnValueTest extends java.lang.Object SourceFile: "ReturnValueTest.java" minor version: 0 major version: 49 Constant pool: --constant pool const #1 = class #2; // cc/lixiaohui/demo/ReturnValueTest const #2 = Asciz cc/lixiaohui/demo/ReturnValueTest; const #3 = class #4; // java/lang/Object const #4 = Asciz java/lang/Object; const #5 = Asciz <init>; const #6 = Asciz ()V; const #7 = Asciz Code; const #8 = Method #3.#9; // java/lang/Object."<init>":()V const #9 = NameAndType #5:#6;// "<init>":()V const #10 = Asciz LineNumberTable; const #11 = Asciz LocalVariableTable; const #12 = Asciz this; const #13 = Asciz Lcc/lixiaohui/demo/ReturnValueTest;; const #14 = Asciz test; const #15 = Asciz ()I; const #16 = class #17; // java/lang/Exception const #17 = Asciz java/lang/Exception; const #18 = Asciz a; const #19 = Asciz I; const # 20 = Asciz and; const #21 = Asciz Ljava/lang/Exception;; const #22 = Asciz SourceFile; const # 23 = Asciz ReturnValueTest.java; { public cc.lixiaohui.demo.ReturnValueTest(); --The construction method will not be analyzed Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcc/lixiaohui/demo/ReturnValueTest; public int test(); Code: Stack=1, Locals=5, Args_size=1 -- [Stack=1 seems to indicate that the stack depth is 1,]Locals=5 indicates that the length of the local variable table is 5, and Args_size=1 indicates that the method has 1 parameter (this) 0: iconst_1 -- push the int value 1 onto the stack 1: istore_1 -- pop the top value of the stack (ie 1) and save it to the second position of the local variable table (the subscript of the local variable table starts from 0, but the 0 position is occupied by the this variable) 2: iload_1 -- push the value of the second position of the local variable table onto the stack 3: istore 4 -- pop the value on the top of the stack and save it to the 5th position of the local variable table (you can see it here) 5: iconst_3 -- (the finally block starts here) push the int value 3 onto the stack 6: istore_1 -- pop the value at the top of the stack (ie 3) and store it in the second position of the local variable table 7: iload 4 -- pushes the fifth position (ie 1) of the local variable table onto the stack 9: ireturn -- return the value at the top of the stack (ie 1), end the method call (the path is try (no exception thrown) -> finally) 10: astore_2 -- store the reference to the top of the stack (here, the exception e caught in the catch block) to the third position of the local variable table 11: iconst_2 -- push the int value 2 onto the stack 12: istore_1 -- pop and store the top value of the stack (ie 2) to the second position of the local variable table 13: iload_1 -- push the value of the second position of the local variable table (ie 2) onto the stack 14: istore 4 -- pop the top value of the stack (ie 2) and save it to the fifth position of the local variable table. The fifth position was originally 1, but now 1 is overwritten and becomes 2 16: iconst_3 -- (the finally block starts here) push the int value 3 onto the stack 17: istore_1 -- pop the value at the top of the stack (ie 3) and store it to the second position of the local variable table 18: iload 4 -- pushes the fifth position (ie 2) of the local variable table onto the stack 20: ireturn -- return the value at the top of the stack (ie 2), end the method call (the path is try (throw Exception or its subclass exception) -> catch -> finally) 21: astore_3 -- store the reference to the top of the stack (here is a non-Exception subclass exception) to the fourth position of the local variable table 22: iconst_3 -- push the int value 3 onto the stack 23: istore_1 -- pop and store the top value of the stack (ie 3) to the second position of the local variable table 24: aload_3 -- push the fourth position of the local variable table (that is, the exception reference) on the stack 25: athrow -- throw the exception at the top of the stack (the path is try (throw non-Exception or its subclass exception) -> finally, or try (throw Exception or its subclass exception) -> catch (throw any exception) -> finally ) Exception table: from to target type 0 5 10 Class java/lang/Exception -- If the execution reaches lines 0-5 (that is, in the try block) and throws java.lang.Exception or its subclasses, jump to line 10 for execution (ie, the catch block) 0 5 21 any -- if execution reaches lines 0-5 (ie, in the try block), a non-java.lang.Exception or its subclass is thrown, and then jumps to line 21 for execution (ie, the finally block) 10 16 21 any -- if any exception is thrown to line 10-16 (that is, in the catch block), jump to line 21 for execution (that is, the finally block) LineNumberTable: --This table is the mapping between the number of source code lines and the number of bytecode lines line 13: 0 line 14: 2 line 19: 5 line 14: 7 line 15: 10 line 16: 11 line 17: 13 line 19: 16 line 17: 18 line 18: 21 line 19: 22 line 20: 24 LocalVariableTable: --This is the local variable table. The combination of start and length can determine the scope of the variable. For example, the scope of this is the entire method. Start Length Slot Name Signature 0 26 0 this Lcc/lixiaohui/demo/ReturnValueTest; -- occupy the first slot (a slot should be 32bits) 2 8 1 a I --I identifies int, occupying the second slot 13 8 1 a I -- occupy the second slot 24 2 1 a I -- occupy the second slot 11 10 2 e Ljava/lang/Exception; -- occupy the third slot }
It can be found that the jvm always puts the return value in the position of the last local variable table, and changing x in finally does not affect the return value.
refer to:
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html