写在前面

本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和文献引用请见100个问题搞定Java虚拟机

解答

方法(Java中称为方法,其他语言一般称为函数)调用主要是通过栈来存储相关的数据,系统就方法调用者和方法如何使用栈做了约定,返回值可以简单认为是通过一个专门的返回值存储器来存储的。

补充

举例说明

上面的描述可能有点抽象,我们通过一个具体的例子来说明方法执行的过程:

public class Sum{
    public static int sum(int a, int b){
        int c = a * b;
        return c;
    }
    
    public static void main(String[] args){
        int d = Sum.sum(1, 2);
        System.out.println(d);
    }
}

当程序在 main 方法调用 Sum.sum 之前,栈的情况大概如图所示。

调用Sum.sum之前的栈示意图

在 main 方法调用 Sum.sum 时,首先将参数 1 和 2 入栈,然后将返回地址(也就是调用方法结束后要执行的指令地址)入栈,
接着跳转到 sum 函数,在 sum 函数内部,需要为局部变量 c 分配一个空间,而参数变量 a 和 b 则直接对应于入栈的数据 1 和 2,在返回之前,返回值保存到了专门的返回值存储器中。

在调用 return 后,程序会跳转到栈中保存的返回地址,即 main 的一条指令地址,而 sum 函数相关的数据会出栈,从而又变回上图中的样子。

main 的下一条指令是根据方法返回值给变量 d 赋值,返回值从专门的返回值存储器中获得。

在Sum.sum内部,准备返回之前的栈示意图

程序执行的基本原理

CPU有一个指令指示器,指向下一条要执行的指令,要么顺序执行,要么进行跳转(条件跳转或无条件跳转)。

具体到Java程序来说就是,程序从 main 方法开始顺序执行,方法调用可以看作一个无条件跳转,跳转到对应方法的指令处开始执行,
碰到 return 语句或者方法结尾的时候,再执行一次无条件跳转, 跳转回调用方,执行调用方法后的下一条指令。

上述流程存在下面的问题

  1. 参数如何传递?
  2. 方法如何知道返回到什么地方?
  3. 方法结果如何传给调用方?

解决思路

使用内存存放这些数据,方法调用方和方法自己就如何存放和使用这些数据达成一个一致的协议或约定。

这个约定就是

操作数栈 VS 寄存器

详情请见我的另一篇博客——JVM为什么采用面向操作数栈而不是寄存器的架构?

Q.E.D.


大数据开发工程师,精通 Spark,擅长 Java 和 Scala