Skip to content

Latest commit

 

History

History
97 lines (62 loc) · 2.8 KB

File metadata and controls

97 lines (62 loc) · 2.8 KB

程序虚拟机的两种实现方式

什么是虚拟机

虚拟机可以分为两种:

  • 系统虚拟机:利用软件来运行一个完整的操作系统,例如可以借助 VirtualBox 来安装 Linux 系统。
  • 程序虚拟机:用于在与平台无关的环境中执行程序,例如 JVM.

本文关注后者。

解释器

解释器是执行程序的一种方式,它接受程序源码,输出源码的执行结果。它与编译的区别可以理解为:

上图为编译器,下图为解释器

但是严格来讲,对于某种语言而言,并无编译型语言与解释型语言之分。例如,Cpp 经常被视为编译型语言,但也有一款名为 Clint 的解释器。

实际上,解释器更像是一个黑箱操作,它为开发者隐藏了编译步骤,从这一角度来看,解释器更像是 编译 + 虚拟机 的结合。

程序虚拟机的含义

此时,程序虚拟机的含义更明确了一些:它是指执行编译产物的一种环境。

c 的编译产物为二进制,它的执行环境为真实的操作系统; v8 下的 JavaScript 编译产物为字节码,它的执行环境为 v8 虚拟机; ...

为了顺利的执行编译产物,程序虚拟机需要实现一套硬件结构,例如 CPU, stack, register 等;此外,在软件层面上,还需要实现一套指令系统。

程序虚拟机的指令集架构

程序虚拟机的指令集可以分为两种实现方式:

  • 栈;
  • 寄存器。

首先来看一段 Java 代码:

public static void main(String[] args) {
    int a = 1;
    int b = 2;
    int c = a + b;
}

首先通过 javac <CodePath>上述代码编译后,再将其产物通过 javap -verbose -p <YourClass> 将其进行反编译看,即得到相应的 Java 字节码:

其字节码中,有如下一段:

字节码片段

其中:

0: iconst_1
1: istore_1

表示创建一个变量 1,然后存储变量 1.

随后:

4: iload_1
5: iload_2
6: iadd
7: isotre_3

表示,加载变量 1 和变量 2, 然后相加,然后存储。

此时,思考一个问题:最后存储的变量 3 为什么是上一次相加的结果?

答案很简单:因为上次相加的值被压回到栈顶,因此可以拿到该值。其过程示例为:

4: iload_1
5: iload_2
6: iadd     --->  result
7: isotre_3       istore_3

上述过程即为栈实现的程序虚拟机(JVM 即为栈实现)

而若是寄存器实现,则可能出现多地址指令,例如 a = b + c 可能变为:

add a, b, c

// 一般形式为:
op dest, src1, src2

上述操作中,dest, src_x 都是存储在寄存器中的。