总结:
基于Stack的虚拟机有JVM、.net的CLR,基于寄存器虚拟机有Lua VM和Dalvik VM
基于Stack的操作数都是保存在Stack数据结构中,栈指针去取出数据、将结果存入栈.
基于寄存器的操作数是存放在CPU的寄存器,指令包含操作数的地址. 劣势(去掉了入栈和出栈的操作,执行得更快,减少了重复计算,指令要长)
Dalvik执行.dex格式的字节码,而JVM执行.class格式的字节码。dex文件格式可以减少整体文件尺寸,提高I/O操作的类查找速度
基于Stack的操作数都是保存在Stack数据结构中,栈指针去取出数据、将结果存入栈.
基于寄存器的操作数是存放在CPU的寄存器,指令包含操作数的地址. 劣势(去掉了入栈和出栈的操作,执行得更快,减少了重复计算,指令要长)
Dalvik执行.dex格式的字节码,而JVM执行.class格式的字节码。dex文件格式可以减少整体文件尺寸,提高I/O操作的类查找速度
Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,防止崩溃
Dalvik虚拟机在android2.2之后使用JIT (Just-In-Time)技术,与传统JVM的JIT并不完全相同
Dalvik虚拟机在android2.2之后使用JIT (Just-In-Time)技术,与传统JVM的JIT并不完全相同
Dalvik主要是完成对象生命周期管理,堆栈管理,线程管理,安全和异常管理,以及垃圾回收等等重要功能。
更多的依赖操作系统的线程调度和管理机制
虚拟机进程Zygote
虚拟机的概念
首先问一个基本的问题,作为一个虚拟机,它最基本的要实现哪些功能?
他应该能够模拟物理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指令包含了操作数地址)。