JVM之方法返回地址详解

网友投稿 1669 2022-10-26

JVM之方法返回地址详解

JVM之方法返回地址详解

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 看代码吧

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() {

http:// 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 解析后的结果

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/javpkKqVa3/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."":(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."":([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 虚拟机实现相关的一些附加信息。例如:对程序调试提供支持的信息。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Light_rtdc- 轻量级分布式实时计算框架
下一篇:2020下半年学习总结
相关文章

 发表评论

暂时没有评论,来抢沙发吧~