虚拟机字节码执行引擎【基于栈的字节码解释执行引擎(二)】

基于栈的指令集与基于寄存器的指令集

Javac编译器输出的字节码指令流,基本上是一种基于栈的指令集架构(Instruction Set Architecture,ISA),字节码指令流里面的指令大部分都是零地址指令,它们依赖操作数栈进行工作。
与之相对的另外一套常用的指令集架构师基于寄存器的指令集,最典型的就是x86的二地址指令集,通俗来讲就是现在主流PC机中物理硬件直接支持的指令集架构,这些指令依赖寄存器进行工作。

两者之间的区别:
以“1+1”为例,

  • 基于栈的指令集如下
iconst_1
iconst_1
iadd
istore_0

两条iconst_1指令连续把另个常量1压入栈后,iadd指令把栈顶的两个值出栈、相加。然后把结果放回栈顶,最后istore_0把栈顶的值放到局部变量表的第0个变量槽中。
这种指令流中的指令通常都是不带参数的,使用操作数栈中的数据作为指令的运算输入,指令的运算结果也存储在操作数栈之中。

  • 基于寄存器的指令集
mov  eax,1
add  eax,1

mov指令把EAX寄存器的值设为1,然后add指令再把这个值加1,结果就保存在EAX寄存器里。这种二地址指令是x86指令集中的主流,每个指令都包含两个单独的输入参数,依赖于寄存器来访问和存储数据。

基于栈的指令集主要优点是可移植,因为寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则不可避免要受到硬件约束。如现在的32位80x86体系的处理器能提供了8个32位的寄存器,而ARMv6体系的处理器则提供了30个32位的通用寄存器,其中前16个用户模式可使用。如使用栈架构的指令集,用户程序不会直接用到这些寄存器,那就可由虚拟机实现来自行决定把一些访问最频繁的数据(程序计数器、栈顶缓存等)放到寄存器中以获取尽量好的性能,这样实现起来也更简单一些。栈机构的指令集还有一些其它优点,如代码相对更加紧凑(字节码每个字节就对应一条指令)、编译器实现更加简单(不需考虑空间分配)等。
主要缺点是理论上执行速度相对来说会稍慢一些,所有主流物理机的指令集都是寄存器架构也从侧面印证了这点。不过这里的执行速度就要局限于解析执行的状态下,如果经过即时编译器输出成物理机上的汇编指令流,那就与虚拟机采用哪种指令的架构没什么关系。
在解释执行时,栈架构指令集的代码虽然紧凑,但完成相同功能所需的指令数量一般会比寄存器架构来的更多。因为出栈、入栈操作本身就产生了相当大量的指令。更重要的是栈实现的内存中,频繁的栈访问也就意味着频繁的内存访问,相对于处理器来说,内存始终是执行速度的瓶颈。尽管虚拟机可采取栈顶缓存的优化方法,把最常用的操作映射到寄存器中避免直接内存访问,但这也只是优化措施而不是解决本质问题的方法。因此由于指令数量和内存访问的原因,导致了栈架构指令集的执行速度会相对慢上一些。

《深入理解Java虚拟机》第三版学习

你可能感兴趣的:(虚拟机字节码执行引擎【基于栈的字节码解释执行引擎(二)】)