深入Java虚拟机之基于栈指令的解释器执行引擎

时间:2022-05-28 14:24:26

解释执行

Java被人定位于“解释执行”的语言。在jdk1.0时,定义还算准确,但后来当主流虚拟机中都包含了即使编译器后,Class文件中的代码
大部分的程序代码到物理机的目标代码或虚拟机能执行的指令集之前,都需要经过以下过程:

深入Java虚拟机之基于栈指令的解释器执行引擎

如今,基于物理机、Java虚拟机,或者非Java的其他高级语言虚拟机的语言,大多数都会遵循这种基于现代经典编译原理的思路,在执行前先对程序源码进行词法分析和语法分析处理,把源码转为抽象语法树。

在Java中,Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程,因为这一部分是在Java虚拟机之外进行的,而解释器在虚拟机的内部,所以Java程序的编译就是半独立的。

基于栈的指令集架构

Java编译器输出的指令流,基本上是一种基于栈的指令集架构,指令流中的指令大部分都是零地址指令,他们依赖操作数栈进行工作。

基于栈的指令集主要优点就是可移植,使用栈架构指令集,用户程序不会直接使用寄存器,就可以由虚拟机实现来自行决定把一些访问最频繁的数据放到寄存器中以获取尽量好的的性能。

栈架构指令集的主要缺点是执行速度相对来说会稍慢一些,主要是因为指令数量和内存访问导致的。

基于栈指令的解释器执行过程

public int calc()
{
    int a=100;
    int b=200;
    int c=300;
    return (a+b)*c;
}  

字节码指令

public int calc();
    Code
    Stack=2.Locals=4,Args_size=1
    0:     bitpush  100
    2:     istroe_1
    3:     sipush   200
    6:     istroe_2
    7:     sipush   300
    10:    istore_3
    11:    iload_1
    12:    iload_2
    13:    iadd
    14:    iload_3
    15:    imul
    16:    ireturn

javap提示这段需要栈深度为2的操作数栈和4个Slot的局部变量空间。

首先执行偏移地址为0的指令,bipush指令将单字节的整型常量池推入操作数栈,100指的是推送的常量值。
深入Java虚拟机之基于栈指令的解释器执行引擎

执行偏移地址为2的指令,istroe_1指令是将操作数栈顶的值出栈并存放到第1个局部变量Slot中,后续4条指令都是在做同样事情。
深入Java虚拟机之基于栈指令的解释器执行引擎

iload_1指令是将局部变量表第1个Slot中的整型值复制到操作数栈顶。
深入Java虚拟机之基于栈指令的解释器执行引擎

iload_2指令同样是将第2个Slot中整型值复制到操作数栈顶。
深入Java虚拟机之基于栈指令的解释器执行引擎

iadd指令的作用是将操作数栈中两个数出栈,做加法,然后将结果重新入栈。
深入Java虚拟机之基于栈指令的解释器执行引擎

iload_3是将局部变量表中第3个Slot入栈,此时操作数栈中为两个整数。
深入Java虚拟机之基于栈指令的解释器执行引擎

ireturn指令是方法返回指令之一,将结束方法执行返回操作数栈顶值
深入Java虚拟机之基于栈指令的解释器执行引擎

上面的过程只是一种概念模型,虚拟机最终会对执行过程进行一些优化来提高性能。主要是虚拟机中解析器和即使编译器都会对输入的字节码进行优化

参考《深入理解Java虚拟机》