JVM之方法返回地址
JVM运行时数据区的虚拟机栈的栈帧中包含了返回地址
当一个方法开始执行后,只有两种方式可以退出这个方法。
第一种方式是执行引擎遇到任意一个方法返回的字节码指令(例如:areturn),这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。 另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用 athrow 字节码指令产生的异常,只要在本方法的异常处理器表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。 无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。
小结一下
虚拟机会使用针对每种返回类型的操作来返回,返回值将从操作数栈出栈并且入栈到调用方法的方法栈帧中,当前栈帧出栈,被调用方法的栈帧变成当前栈帧,程序计数器将重置为调用这个方法的指令的下一条指令。
方法返回地址以及栈帧中的附加信息
方法返回地址
1 点睛存放调用该方法的 pc 寄存器的值。
一个方法的结束,有两种方式。
正常执行完成。 出现未处理的异常,非正常退出。无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
当一个方法开始执行后,只有两种方式可以退出这个方法。
a 执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口。
一个方法在正常调用完成之后,究竟需要使用哪一个返回指令,还需要根据方法返回值的实际数据类型而定。 在字节码指令中,返回指令包含ireturn(当返回值是boolean,byte,char,short和int类型时使用),lreturn(Long类型),freturn(Float类型),dreturn(Double类型),areturn。另外还有一个return指令声明为void的方法,实例初始化方法,类和接口的初始化方法使用。b 在方法执行过程中遇到异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,简称异常完成出口。
方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码
2 看代码吧
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
import java.io.FileReader; import java.io.IOException; import java.util.Date; /** * 返回指令包含ireturn(当返回值是 boolean、byte、char、short和int类型时使用)、 * lreturn、freturn、dreturn以及areturn, * 另外还有一个return指令供声明为void的方法、 * 实例初始化方法、类和接口的初始化方法使用。 */ public class ReturnAddressTest { public boolean methodBoolean() { return false ; }
public byte methodByte() { return 0 ; }
public short methodShort() { return 0 ; }
public char methodChar() { return 'a' ; }
public int methodInt() { return 0 ; }
public long methodLong() { return 0L; }
public float methodFloat() { return 0 .0f; }
public double methodDouble() { return 0.0 ; }
public String methodString() { return null ; }
public Date methodDate() { return null ; }
public void methodVoid() { }
static { int i = 10 ; }
public void method2() { methodVoid(); try { method1(); } catch (IOException e) { e.printStackTrace(); } }
public void method1() throws IOException { FileReader fis = new FileReader( "atguigu.txt" ); char [] cBuffer = new char [ 1024 ]; int len; while ((len = fis.read(cBuffer)) != - 1 ) { String str = new String(cBuffer, 0 , len); System.out.println(str); } fis.close(); } } |
3 解析后的结果
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
public boolean methodBoolean(); descriptor: ()Z flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : iconst_0 1 : ireturn LineNumberTable: line 15 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public byte methodByte(); descriptor: ()B flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : iconst_0 1 : ireturn LineNumberTable: line 19 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public short methodShort(); descriptor: ()S flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : iconst_0 1 : ireturn LineNumberTable: line 23 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public char methodChar(); descriptor: ()C flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : bipush 97 2 : ireturn LineNumberTable: line 27 : 0 LocalVariableTable: Start Length Slot Name Signature 0 3 0 this Lcom/atguigu/java3/ReturnAddressTest;
public int methodInt(); descriptor: ()I flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : iconst_0 1 : ireturn LineNumberTable: line 31 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public long methodLong(); descriptor: ()J flags: ACC_PUBLIC Code: stack= 2 , locals= 1 , args_size= 1 0 : lconst_0 1 : lreturn LineNumberTable: line 35 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public float methodFloat(); descriptor: ()F flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : fconst_0 1 : freturn LineNumberTable: line 39 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public double methodDouble(); descriptor: ()D flags: ACC_PUBLIC Code: stack= 2 , locals= 1 , args_size= 1 0 : dconst_0 1 : dreturn LineNumberTable: line 43 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public java.lang.String methodString(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : aconst_null 1 : areturn LineNumberTable: line 47 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public java.util.Date methodDate(); descriptor: ()Ljava/util/Date; flags: ACC_PUBLIC Code: stack= 1 , locals= 1 , args_size= 1 0 : aconst_null 1 : areturn LineNumberTable: line 51 : 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest;
public void methodVoid(); descriptor: ()V flags: ACC_PUBLIC Code: stack= 0 , locals= 1 , args_size= 1 0 : return LineNumberTable: line 56 : 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcom/atguigu/java3/ReturnAddressTest;
public void method2(); descriptor: ()V flags: ACC_PUBLIC Code: stack= 1 , locals= 2 , args_size= 1 0 : aload_0 1 : invokevirtual # 2 // Method methodVoid:()V 4 : aload_0 5 : invokevirtual # 3 // Method method1:()V 8 : goto 16 11 : astore_1 12 : aload_1 13 : invokevirtual # 5 // Method java/io/IOException.printStackTrace:()V 16 : return Exception table: from to target type 4 8 11 Class java/io/IOException LineNumberTable: line 63 : 0 line 65 : 4 line 68 : 8 line 66 : 11 line 67 : 12 line 69 : 16 LocalVariableTable: Start Length Slot Name Signature 12 4 1 e Ljava/io/IOException; 0 17 0 this Lcom/atguigu/java3/ReturnAddressTest; StackMapTable: number_of_entries = 2 frame_type = 75 /* same_locals_1_stack_item */ stack = [ class java/io/IOException ] frame_type = 4 /* same */
public void method1() throws java.io.IOException; descriptor: ()V flags: ACC_PUBLIC Code: stack= 5 , locals= 5 , args_size= 1 0 : new # 6 // class java/io/FileReader 3 : dup 4 : ldc # 7 // String atguigu.txt 6 : invokespecial # 8 // Method java/io/FileReader."<init>":(Ljava/lang/String;)V 9 : astore_1 10 : sipush 1024 13 : newarray char 15 : astore_2 16 : aload_1 17 : aload_2 18 : invokevirtual # 9 // Method java/io/FileReader.read:([C)I 21 : dup 22 : istore_3 23 : iconst_m1 24 : if_icmpeq 50 27 : new # 10 // class java/lang/String 30 : dup 31 : aload_2 32 : iconst_0 33 : iload_3 34 : invokespecial # 11 // Method java/lang/String."<init>":([CII)V 37 : astore 4 39 : getstatic # 12 // Field java/lang/System.out:Ljava/io/PrintStream; 42 : aload 4 44 : invokevirtual # 13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 47 : goto 16 50 : aload_1 51 : invokevirtual # 14 // Method java/io/FileReader.close:()V 54 : return LineNumberTable: line 72 : 0 line 73 : 10 line 75 : 16 line 76 : 27 line 77 : 39 line 78 : 47 line 79 : 50 line 80 : 54 LocalVariableTable: Start Length Slot Name Signature 39 8 4 str Ljava/lang/String; 0 55 0 this Lcom/atguigu/java3/ReturnAddressTest; 10 45 1 fis Ljava/io/FileReader; 16 39 2 cBuffer [C 23 32 3 len I StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 16 locals = [ class java/io/FileReader, class "[C" ] frame_type = 252 /* append */ offset_delta = 33 locals = [ int ] Exceptions: throws java.io.IOException
static {}; descriptor: ()V flags: ACC_STATIC Code: stack= 1 , locals= 1 , args_size= 0 0 : bipush 10 2 : istore_0 3 : return LineNumberTable: line 59 : 0 line 60 : 3 LocalVariableTable: Start Length Slot Name Signature } |
说明
可以观察一下各种方法的 return 字节码指令到底是什么。
体会一下异常表。
Exception table:
from to target type
4 8 11 Class java/io/IOException
本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。
正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。
一些附加信息
栈帧中还允许携带与 Java 虚拟机实现相关的一些附加信息。例如:对程序调试提供支持的信息。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
原文链接:https://HdhCmsTestjianshu测试数据/p/ecfcc9fb1de7