本章我们来重点分析下局部变量表和操作数栈,并测试本章代码。
局部变量表和操作数栈实例详解
下面以圆形的周长为例进行分析,首先简单的Java代码如下:
编译该类,class文件如下图所示:
可以看出circumference()方法的局部变量表max_locals大小是3,操作数栈max_stack深度是2。再使用javap -c对代码进行反汇编来查看运行指令:
假设调用可以看出circumference()方法时,传递给它的参数时1.6f,方法开始执行前,栈帧的状态如下图:
第一条指令是ldc,它把3.14f推入栈顶,此时栈帧状态如下图所示:
注意一下,图上把局部变量表和操作数栈过去的状态也列出来了,最下面一条时当前状态。下一条指令是fstore_1,它把栈顶的3.14f弹出,放到#1号局部变量中,栈帧的状态如图所示:
第三条指令fconst_2把2.0f推到栈顶,栈帧的状态如下图所示:
第四条指令fload_1把#1号局部变量推入栈顶(更确切地说应该是复制),栈帧的状态如下图所示:
由于fload_1指令是把3.14f复制到栈顶,所以3.14f在局部变量表中。下一条指令是fmul,它把栈顶的两个浮点数弹出、相乘,然后再把结果推入栈顶,栈帧的状态如下图所示:
第六条指令fload_0指令把#0号局部变量推入栈顶,栈帧的状态如下图所示:
第七条指令fmul,继续把栈顶的两个浮点数弹出、相乘,然后再把结果推入栈顶,栈帧的状态如下图所示:
第八条指令fload_2把操作数栈顶的float值弹出,放入#2号局部变量表,栈帧的状态如下图所示:
第九条指令fload_2把#2号局部变量推入操作数栈顶,栈帧的状态如下图所示:
最后freturn指令把操作数栈顶的float变量弹出,返回给方法调用者,,栈帧的状态如下图所示:
我们大致理了一下circumference()方法的运行过程,后面会学到更多的Java虚拟机指令集。不过,上述第八条和第九条指令集好像做了无用功,下面我们尝试调整下circumference()方法:
此时再看运行指令:
可以看出少了原来的第八条、第九条指令,不管从哪个角度看,少两条指令总归是好的吧,看来以后写代码能注意就注意。
测试
本节简单测试局部变量表和操作数栈,修改main.go文件:
|
|
运行时数据区的学习暂且就到这里,我们初步实现了Tread、Stack、Frame、OperandStack和LocalVars等线程私有的运行时数据区。