虚拟机的概念
首先问一个基本的问题,作为一个虚拟机,它最基本的要实现哪些功能?
他应该能够模拟物理CPU对操作数的移进移出,理想状态下,它应该包含如下概念:(1)将源码编译成VM指定的字节码。
(2)包含指令和操作数的数据结构(指令用于处理操作数作何种运算)。
(3)一个为所有函数操作的调用栈。
(4)一个“指令指针(Instruction Point ---IP)”:用于指向下一条将要执行的指令。
(5)一个虚拟的“CPU”--指令的派发者:
1)取指:获取下一条指令(通过IP获取)
2)译码:对指令进行翻译,将要作何种操作。
3)执行:执行指令。
以上是CPU的三级流水线操作,实际上五级流水线还包括回写,即把执行后生成的结果回写进存储器。
有两种基本的方法实现虚拟机:基于Stack的和基于Register的,比如基于Stack的虚拟机有JVM、.net的CLR,这种基于Stack实现虚拟机是一种广泛的实现方法。
而基于Register的虚拟机有Lua VM(是Lau编程语言的虚拟机)和Dalvik VM。这两种虚拟机实现的不同主要在于操作数和结果的存储和检索机制不一样。
Stack-Based虚拟机
一个基于Stack的虚拟机会通过IP来获取操作数,其操作数都是保存在Stack数据结构中,从栈中取出数据、计算然后再将结果存入栈中(LIFO,Last in first out)。如下就是一个典型的计算20+7在栈中的计算过程:
2、POP 7
3、ADD 20, 7, result
4、PUSH result
Register-Based虚拟机
基于寄存器的虚拟机,它们的操作数是存放在CPU的寄存器的。没有入栈和出栈的操作和概念。但是执行的指令就需要包含操作数的地址了,也就是说,指令必须明确的包含操作数的地址,这不像栈可以用栈指针去操作。比如如下的加法操作:
正如前面所说,基于寄存器的VM没有入栈和出栈的操作。所以加法指令只需要一行就够了,但是不像Stack-Based一样,我们需要明确的制定操作数R1、R2、R3(这些都是寄存器)的地址。这种设计的有点就是去掉了入栈和出栈的操作,并且指令在寄存器虚拟机执行得更快。
基于寄存器得虚拟机还有一个优点就是一些在基于Stack的虚拟机中无法实现的优化,比如,在代码中有一些相同的减法表达式,那么寄存器只需要计算一次,然后将结果保存,如果之后还有这种表达式需要计算就直接返回结果。这样就减少了重复计算所造成的开销。
当然,寄存器虚拟机也有一些问题,比如虚拟机的指令比Stack vm指令要长(因为Register指令包含了操作数地址)。