欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jvm深度学习(3):java方法的运行与虚拟机栈

程序员文章站 2022-05-12 14:24:28
...

方法的执行

虚拟机栈是线程运行 java 方法所需的数据,指令、返回地址。其实在我们实际的代码中,一个线程是可以运行多个方法的。 比如:

jvm深度学习(3):java方法的运行与虚拟机栈

这段代码很简单,就是起一个 main 方法,在 main 方法运行中调用 A 方法,A 方法中调用 B 方法,B 方法中运行 C 方法。
我们把代码跑起来,线程 1 来运行这段代码, 线程 1 跑起来,就会有一个对应 的虚拟机栈,同时在执行每个方法的时候都会打包成一个栈帧。 比如 main 开始运行,打包一个栈帧送入到虚拟机栈。

执行main方法

jvm深度学习(3):java方法的运行与虚拟机栈

执行A()

jvm深度学习(3):java方法的运行与虚拟机栈

执行B()

jvm深度学习(3):java方法的运行与虚拟机栈

执行C()

jvm深度学习(3):java方法的运行与虚拟机栈

C 方法运行完了,C 方法出栈,接着 B 方法运行完了,B 方法出栈、接着 A 方法运行完了,A 方法出栈,最后 main 方法运行完了,main 方法这个栈帧就 出栈了。
这个就是 Java 方法运行对虚拟机栈的一个影响。虚拟机栈就是用来存储线程运行方法中的数据的。而每一个方法对应一个栈帧。

 

操作数栈:执行引擎的一个工作区。

操作系统: CPU + 缓存 + 主内存

虚拟机(模拟版的操作系统): 执行引擎 + 操作数栈 +栈、 堆

 

 

方法在栈帧中是如何操作运行的?

先写简单的代码如下:

package com.imooc.firstappdemo.jvm;

/**
 * @author TofuCai
 * 栈帧的执行对内存的影响
 */
public class TofuCai {
    public int work() throws Exception {
        int x = 1;
        int y = 2;
        int z = (x+y)*10;
        return z;
    }

    public static void main(String[] args) throws Exception{
        TofuCai tofuCai = new TofuCai();
        tofuCai.work();
    }
}

cmd到TofuCai.class文件目录下

jvm深度学习(3):java方法的运行与虚拟机栈

选用下面命令进行反汇编 javap -c TofuCai.class

jvm深度学习(3):java方法的运行与虚拟机栈

执行结果如下

jvm深度学习(3):java方法的运行与虚拟机栈

 

首先大概清楚方法的程序流程

jvm深度学习(3):java方法的运行与虚拟机栈

根据字节码对于work()方法的执行流程进行解析:

jvm深度学习(3):java方法的运行与虚拟机栈

首先需要知道字节码各个指令的含义,这里有个腾讯提供的文档可以帮助我们解析,字节码助记码解释地址:https://cloud.tencent.com/developer/article/1333540

根据文档查得 iconst指令:

jvm深度学习(3):java方法的运行与虚拟机栈

根据文档查得 istore指令: 

jvm深度学习(3):java方法的运行与虚拟机栈

由此我们可以得知jvm深度学习(3):java方法的运行与虚拟机栈这两个指令得作用是:

1、将一个常量值为1得数压到操作数栈;

jvm深度学习(3):java方法的运行与虚拟机栈

2、将当前操作数栈的值存储到下标为1的局部变量表中

jvm深度学习(3):java方法的运行与虚拟机栈

注意:

        1、此时局部变量表中首位有个this表示的是当前类的实例,如果该方法为静态方法那么没有this;

        2、iconst后面跟的值是数值,而istore后面跟的是值局部表量表的位置。

上面两个指令对应到代码就是:int x = 1;

同理,jvm深度学习(3):java方法的运行与虚拟机栈指令也是同上一样的,先将数值为2的数压入操作数栈,然后将该数值存入到下标为2的局部变量表。对应的代码就是:int y = 2;

下面看看接下来的流程:

jvm深度学习(3):java方法的运行与虚拟机栈

首先看iload的指令:

jvm深度学习(3):java方法的运行与虚拟机栈

iadd指令:

jvm深度学习(3):java方法的运行与虚拟机栈

bipush指令:

jvm深度学习(3):java方法的运行与虚拟机栈

imul指令:

jvm深度学习(3):java方法的运行与虚拟机栈

综上流程如下:

       1、从局部变量中将下标为1的数压入到操作数栈中;

       2、从局部变量中将下标为2的数压入到操作数栈中;

       3、将操作数栈的数值进行加法运算;

       4、将常量10压入到操作数栈中;

       5、将操作数栈的数值进行乘法运算;

       6、将操作数栈的数值存入到局部变量表中

以上6步执行流程对应到代码为:int z = (x+y)*10;

注意:无论是iadd还是imul事实上他们都分为三步进行执行:1、操作数栈的两个数依次出栈到执行引擎(相当于jvm的cpu);2、将这两个数进行运算;3、将运算得出的结果压入到操作数栈的栈顶。

这里有个点特别有意思,执行引擎在执行完运算后,并没有直接将结果存入局部变量表,而是又压入到了操作数栈,这是为什么呢?

我们知道,jvm的执行引擎类似CPU的角色。既然如此,执行引擎就没有存储数据的功能,而操作数栈类似一个高速缓存,因此存储任务就交给了操作数栈最恰当不过,当进行大批量数据运算,不断进行中间数据的入栈出栈,极大的提高数据运算效率。

剩下最后两个指令:jvm深度学习(3):java方法的运行与虚拟机栈

根据文档得ireturn指令:

jvm深度学习(3):java方法的运行与虚拟机栈

   执行流程:

         1、我们知道iload_3是将局部变量表中下标为3的数据压入到操作数栈中;

         2、执行引擎执行将操作数栈返回给调用方。

到此整个work()方法执行结束。

 

结语:

为何需要操作数栈?

操作数栈在整个jvm中扮演着高速缓存的角色,由于jvm的执行引擎不能够存储数据,这就需要一个高速缓存能够能执行引擎快速存放他的执行中间结果,并快速获取。所以操作数栈是执行引擎在执行过程极其重要的部分。