什么是虚拟机?
虚拟机是对由本机资源支持的本机机器的抽象。它的工作是将特定于语言的代码转换为兼容在多个平台上运行的格式,独立于底层硬件。
例如,JVM运行 Java 字节码并在多个平台上产生相同的输出;例如,在 Windows 机器上生成的字节码将能够在 UNIX 机器上运行的 JVM 上运行。
注意: JVM 不独立于平台。它是用 C/C++ 编写的,并且与平台相关。您总是需要一些东西来与底层架构进行通信。
虚拟机应该能够执行物理 CPU 的所有操作。这些包括:
- 将源代码编译为 VM 特定代码。
- 包含操作数和指令的数据结构。
- 指令指针。
- 虚拟 CPU。
我们将研究实现虚拟机的两种方法:
- 基于堆栈的架构。
- 基于寄存器的架构。
基于堆栈的架构
堆栈是一种后进先出的数据结构,这意味着最后出现的内容首先被计算。如果您上过计算机体系结构课程,您就会熟悉算术运算在基于堆栈的体系结构中的工作原理。
让我们举个例子。考虑下面给出的堆栈:
现在,为了计算前两个操作数的和,命令链将是:
- 流行 20。
- 流行 7.
- 加 20 + 7。
- 将结果压入堆栈。
下面是实际的CPU指令:
- POP 20。
- 流行音乐7。
- 添加。
请注意,对于两个整数的加法,我们生成了三行代码。
优点
- 指令长度较短。
- 快速操作,因为堆栈指针在弹出后立即指向下一个内存位置。
- 由于堆栈指针,不需要显式内存引用。
缺点
- 程序的大小增加。
基于寄存器的架构
寄存器是处理器的一部分,可以保存各种类型的数据,例如操作数、指令、内存位置等。
寄存器可以存储的指令长度取决于机器的体系结构。例如,64 位机器具有可以保存 64 位指令的寄存器。
以下是基于 MIPS32 寄存器的架构中指令的格式:
现在,如果我们要执行与基于堆栈的寄存器中相同的加法操作,它将如下所示:
- 添加 R1、R2、R3
该指令将R2和R3相加并将结果存储在R1中。注意我们如何用一条指令完成操作。这是基于寄存器的体系结构相对于基于堆栈的体系结构的主要优点。
平均而言,基于寄存器的架构运行的指令比基于堆栈的架构少 47%。然而,寄存器代码比基于堆栈的代码大25%。
当我们在下一节讨论 Android 中的 ART 与 DVM 以及 JVM 与 DVM 时,基于堆栈的架构和基于寄存器的架构的知识将会派上用场。
JVM 与 DVM
虚拟机
Java字节码可以在任何能够运行JVM的机器上运行。
下面是JVM的架构:
它具有基于堆栈的架构。基于堆栈的架构易于实现,并且对底层硬件几乎没有任何假设。它可以在寄存器较少的机器上运行。JVM 只能运行 .class 文件并使用 JIT 编译器。
数字虚拟机
DVM(Dalvik 虚拟机)是 Dan Bornstein 及其团队考虑到移动设备的限制而创建的。它是一个特定用途的虚拟机,专为移动设备创建。
与 JVM 不同,DVM 使用基于寄存器的架构,运行 .dex 文件(并且可以将 .class 文件转换为 .dex 文件)。与 JVM 类似,它使用 JIT 编译器。
AOT 与 JIT
本节对于理解 Android 中 ART 与 DVM 的比较非常重要。ART 和 DVM 的主要区别在于 ART 使用 AOT 编译;而DVM使用JIT编译。
最近,ART 开始混合使用 AOT 和 JIT。我们将在后面的部分中对此进行研究。
准时生产
- JIT(Just In Time)编译器在运行时将源代码编译为机器代码。这意味着每次运行您的应用程序时,.dex 文件的一部分都会动态转换。
- 随着执行的继续,更多的代码被编译和缓存。
- 每个文件都是单独编译的。
- JIT 编译器可以优化旧程序,因为 JIT 编译过程得到了优化。对于 AOT 编译来说情况并非如此,因为源代码是预先编译的。
- 适应运行时指标。JIT 编译器不仅可以查看代码和目标系统,还可以查看代码的使用方式。它可以检测正在运行的代码,并根据方法参数恰好具有的值等来决定如何优化。