JVM之方法返回地址详解


Posted in Java/Android onFebruary 28, 2022

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),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,简称异常完成出口。

方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码

JVM之方法返回地址详解

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Java/Android 相关文章推荐
Java输出Hello World完美过程解析
Jun 13 Java/Android
JPA如何使用entityManager执行SQL并指定返回类型
Jun 15 Java/Android
Java框架入门之简单介绍SpringBoot框架
Jun 18 Java/Android
SpringCloud Alibaba项目实战之nacos-server服务搭建过程
Jun 21 Java/Android
SpringBoot读取Resource下文件的4种方法
Jul 02 Java/Android
java调用Restful接口的三种方法
Aug 23 Java/Android
logback 实现给变量指定默认值
Aug 30 Java/Android
深入解读Java三大集合之map list set的用法
Nov 11 Java/Android
OpenCV实现普通阈值
Nov 17 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 Java/Android
Java由浅入深通关抽象类与接口(下篇)
Apr 26 Java/Android
IDEA中sout快捷键无效问题的解决方法
Jul 23 Java/Android
解决persistence.xml配置文件修改存放路径的问题
Feb 24 #Java/Android
Java实现学生管理系统(IO版)
alibaba seata服务端具体实现
Feb 24 #Java/Android
java协程框架quasar和kotlin中的协程对比分析
Feb 24 #Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 #Java/Android
正则表达式拆分url实例代码
Feb 24 #Java/Android
mybatis源码解读之executor包语句处理功能
Feb 15 #Java/Android
You might like
dedecms模板标签代码官方参考
2007/03/17 PHP
Drupal7中常用的数据库操作实例
2014/03/02 PHP
查找php配置文件php.ini所在路径的二种方法
2014/05/26 PHP
微信API接口大全
2015/04/15 PHP
php+jquery+html实现点击不刷新加载更多的实例代码
2016/08/12 PHP
Laravel 简单实现Ajax滚动加载示例
2019/10/22 PHP
PHP设计模式(八)装饰器模式Decorator实例详解【结构型】
2020/05/02 PHP
THINKPHP5分页数据对象处理过程解析
2020/10/28 PHP
javascript实现unicode和字符的互相转换
2007/07/18 Javascript
javascript FormatNumber函数实现方法
2008/12/30 Javascript
javascript中删除指定数组中指定的元素的代码
2011/02/12 Javascript
jQuery插件Skippr实现焦点图幻灯片特效
2015/04/12 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
2015/11/19 Javascript
javascript实现checkbox复选框实例代码
2016/01/10 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
如何编写jquery插件
2017/03/29 jQuery
vue如何引用其他组件(css和js)
2017/04/13 Javascript
说说如何利用 Node.js 代理解决跨域问题
2019/04/22 Javascript
Python开发如何在ubuntu 15.10 上配置vim
2016/01/25 Python
Python使用一行代码获取上个月是几月
2018/08/30 Python
ubuntu 18.04搭建python环境(pycharm+anaconda)
2019/06/14 Python
python 怎样将dataframe中的字符串日期转化为日期的方法
2019/09/26 Python
python创建学生成绩管理系统
2019/11/22 Python
css3 旋转按钮 使用CSS3创建一个旋转可变色按钮
2012/12/31 HTML / CSS
CSS3标注引用的出处和来源的方法
2020/02/25 HTML / CSS
米兰网婚纱礼服法国网上商店:Milanoo法国
2016/08/20 全球购物
BLACKMORES澳洲官网:澳大利亚排名第一的保健品牌
2018/09/27 全球购物
医院护士求职自荐信格式
2013/09/21 职场文书
产品销售员岗位职责
2013/12/18 职场文书
幼儿园师德演讲稿
2014/05/06 职场文书
病人写给医生的感谢信
2015/01/23 职场文书
征求意见函
2015/06/05 职场文书
2019年暑期安全广播稿!
2019/07/03 职场文书
小学语文教师竞聘演讲稿范文
2019/08/09 职场文书
Android开发EditText禁止输入监听及InputFilter字符过滤
2022/06/10 Java/Android
TS 类型收窄教程示例详解
2022/09/23 Javascript