java反汇编_对于Java方法的运行与虚拟机栈的深度学习

方法的执行

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

5f72aee3e7954de4c7ad627eed53aca3.png

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

执行main方法

1bdbe20e8702b5c62d8c9177e70082a6.png

执行A()

e14ae6d7703b6cdfcf4f5bf72ba63d59.png

执行B()

0f29a8b076453f0f58315efbe4f0f6e7.png

执行C()

f594094a86b3ecaffdc2330259e0e45c.png

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

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

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

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

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

先写简单的代码如下:

d1c8dbd68ef8da21dd39b2c8ad408cf6.png

cmd到TofuCai.class文件目录下

c3873596699946771c4a252ba7529402.png

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

8ba340c3b56647bdb163cc1030aafb37.png

执行结果如下

91d5b129c9b9804a3625f090aa21c15c.png

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

d14525e28867b4f2c05600486db39e26.png

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

cfb17aee73ebb25c539bd763b5f69171.png

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

根据文档查得 iconst指令:

ce00f8504564d3ab3129b27e274af723.png

根据文档查得 istore指令:

49a3508b703510e9f5d432fc562089aa.png

由此我们可以得知

d6cc94e23328dcba3b8c41d17d87d9b6.png

这两个指令的作用是:

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

02873bdf6903bd2d0517a37d5c0917f4.png

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

2209730ece9c59e2ec134c1827bc7866.png

注意:

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

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

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

同理,

43ea15d1a709669b33eb28a56970fdb7.png

指令也是同上一样的,先将数值为2的数压入操作数栈,然后将该数值存入到下标为2的局部变量表。对应的代码就是:int y = 2;

下面看看接下来的流程:

e9838ce2cc8752cd9cae7650bfc086c0.png

首先看iload的指令:

339d3bfe0cf4e877f0db68c9a3004566.png

iadd指令:

8f052f299fa676f187b2e9919d0d2a40.png

bipush指令:

1d63a436f3ec91e50ebfff9d43fafb81.png

imul指令:

b93bbd1d7c291193d364a1d48fc510bf.png

综上流程如下:

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

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

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

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

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

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

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

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

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

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

剩下最后两个指令:

0f0fa92e3acfd50d75bf0651dc36933b.png

根据文档得ireturn指令:

612579b4b22659965971db933c306139.png

执行流程:

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

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

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

结语:

为何需要操作数栈?

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值