JVM之方法返回地址详解
作者:_晓__ 发布时间:2022-05-02 07:58:27
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() {
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/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://www.jianshu.com/p/ecfcc9fb1de7
猜你喜欢
- 关于 swagger 本文不再赘述,网上文章很多。本文要讲的是Knife4j3.0.3 整合SpringBoot 2.6.4,因为 knif
- MyBatis插入Insert、InsertSelective的区别逆向自动生成的mybatis对应配置Mapper文件里面,有两个方法,分
- 本文实例为大家分享了java实现递归菜单树的具体代码,供大家参考,具体内容如下1.表结构SET FOREIGN_KEY_CHECKS=0;-
- 摘要本文主要讲解mall通过整合SpringSecurity和JWT实现后台用户的登录和授权功能,同时改造Swagger-UI的配置使其可以
- C#串口模块的使用。使用VS .net框架下WinForm程序应用开发。C#开发的串口通信小工具。相比于QT添加的串口类,WinForm是通
- 一般而言,一个项目部署的由:拉取代码->构建->测试->打包->部署等过程组成,如果我们经常需要部署项目,特别是在微
- 请求参数解析客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServlet的doPatch(
- ofType和javaType的区别JavaType和ofType都是用来指定对象类型的,但是JavaType是用来指定pojo中属性的类型
- 最近一段时间,大家在用 Spring Security OAuth2 时可能发现有很多类过期了。大家在选择 OAuth2 依赖的时候,可能也
- 先给大家展示下效果图:1、验证码生成类:import java.util.Random;import java.awt.imag
- SWT中没有AWT的BorderLayout布局管理器。下面是SWT下的自定义实现: BorderLayout.java package s
- 一、# List泛型集合集合是OOP中的一个重要概念,C#中对集合的全面支持更是该语言的精华之一。为什么要用泛型集合?在C# 2.0之前,主
- 目录规则(来自百度百科,康威生命游戏词条)控制台实现的关键接口代码实现规则(来自百度百科,康威生命游戏词条)游戏开始时,每个细胞随机地设定为
- 好几天没有跟进Spring MVC的学习了,之前看了点源码都忘的差不多了。这次就跟着之前的问题,继续总结下Spring MVC中的小知识。u
- 程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间
- 一、背景我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主
- 1、简介双重检查锁定(也叫做双重检查锁定优化)是一种软件设计模式。它的作用是减少延迟初始化在多线程环境下获取锁的次数,尤其是单例模式下比较突
- 本文实例为大家分享了Java实现员工管理系统的具体代码,供大家参考,具体内容如下本系统主要练习到的相关内容: 1、 流程控制语句 2、 类、
- JoinPoint的getSignature方法在使用springboot写aop的时候,有个JoinPoint类,用来获取代理类和被代理类
- 冒泡排序算法演示图:public static void bubbleSort(int[] array) { &