目录
前置知识
引子
基于寄存器的设计模式
基于栈的设计模式
一个简单的例子
如何查看局部变量表?
实例方法中的局部变量表
结论
前置知识
阅读本文需要对以下知识有所了解:
* 栈
* 汇编
* Java 基础
* 逆波兰表达式(有学过的同学阅读本文毫无障碍)
引子
基于寄存器的设计模式
就我们所熟知的 x86 或 ARM 指令集来说, 其对数据的操作都是基于寄存器. 例如, 要对两个数执行加法操作则需要将这两个数分别送入两个寄存器再执行加法操作, 这也符合我们对于编程语言认知, 更加易于理解.
基于栈的设计模式
基于栈的设计模式则是将数据存放在栈中, 在需要使用的时候将栈顶的数据出栈, 并执行相应的操作.
举例来说, 在 JVM 中 执行 a = b + c 的字节码执行过程中操作数栈以及局部变量表的变化如下图所示.
局部变量表中存储着 a,b,c 三个局部变量, 首先将 b 和 c 分别入栈
将栈顶的两个数出栈执行加法操作, 并将结果保存至栈顶, 之后将栈顶的数出栈赋值给 a
一个简单的例子
在上一节中我们了解了栈与局部变量表是如何配合完成一次加法操作的, 这一节我们将对局部变量表进行深入的研究.
如何查看局部变量表?
我们可以通过反编译 class 文件的方式查看局部变量表, 不过在这里更加推荐使用 IDEA 的 jclasslib 插件 (直接搜就有) 查看字节码, 因为其设计更加人性化, 更加友好.
实例方法中的局部变量表
我们知道在实例方法中我们可以直接访问实例的成员变量或函数, 而不需要通过 this 来引用, 这是如何实现的?
像这种问题直接动手写个测试类, 反编译一下结果自然就清晰了.
首先来一个简单的类
- public class T {
- private int a = 0;
- public void add(int b,int c){
- a = b + c;
- }
- }
其次, 将该类选中, 然后在 view 中选中 showbytecode with jclasslib
效果如下:
选择我们关注的 Methods 中 add 方法, 注意观察图中高亮的部分
原来, JVM 在编译代码的时候, 偷偷在局部变量表中添加了一个 this 引用(很明显 this 保存的实例的引用), 这也是我们为什么可以在方法中访问实例中的成员变量的原因, 证明如下
图中节码的简要解释如下:
0)aload_0 将 this 的引用入栈 (aload_0 即将局部变量表中索引为 0 的引用压到操作数栈中)
1)iload_1 将参数 b 入栈 (将局部变量中的索引为 1 的整数压到操作数栈中)
2)iload_2 将参数 c 入栈
此时栈的内容有(0 为栈顶)
0.c
1.b
2.this
3)iadd 将栈顶的两个数相加, 并将结果保存至栈顶, 此时栈的内容为
0.b+c
1.this
4). putfield 将栈顶的两个值出栈, 第一个值 (b+c) 赋值给第二个值 (this) 的对应的成员变量(是的, 没错即使是赋值也要执行两次出栈操作)
putfield 的说明如下(注意图中的高亮部分):
地址:
结论
基于栈的指令集系统可以很方便的做到平台无关性(x86,ARM), 但也降低了性能, 这也是为啥 Java 性能比 C 低原因.(操作寄存器快, 还是操作栈快? 哈哈哈哈哈哈哈哈哈哈哈)
来源: https://www.cnblogs.com/kesan/p/11368934.html