Inferno虚拟机的设计

2020-07-21 21:10:08

Phil WinterBottom和Rob Pike Bell Labs,Lucent Technologies{Philw,rob}@plan9.bell-labs.com虚拟机是现代便携式环境(如Inferno和Java)的重要组件,因为它们提供独立于体系结构的可执行代码表示。它们的性能对这类环境的成功至关重要,但它们很难设计好,因为它们受相互冲突的目标的影响。一方面,它们提供了一种隐藏指令体系结构之间差异的方法;另一方面,它们必须在各种底层机器上有效地实现。通过对Inferno和Java虚拟机的工程设计和发展进行比较,可以深入了解它们在设计和实现方面的权衡。我们认为,虚拟机的设计应该植根于现代处理器的本质,而不是语言解释器,着眼于即时编译,而不是解释或特殊用途的硅。1995年初,我们开始将Plan9操作系统[1]的思想应用于更广泛的设备和网络。由此产生的系统Inferno[2]是一个小型操作系统和执行环境,支持跨多种处理器和操作系统的应用程序可移植性。由于没有从Oak项目的技术中意识到建立Java的当代工作[3],我们独立地得出结论,虚拟机(VM)是这样一个系统的必要组件[4]。由于处理器速度的提高和动态编译器的可行性,VM可以足够快地执行,从而在经济上是可行的。名为Dis的Inferno虚拟机在设计上有几个不同寻常的方面:指令集、模块系统和垃圾收集器。Dis指令集与现有处理器的体系结构非常匹配。指令的形式如下:src1和dst操作数指定一般地址或任意大小的常量,而src2操作数被限制为较小的常量和堆栈偏移量,以减少代码空间。每个操作数在执行过程的堆栈帧或其模块的全局数据中指定一个地址。操作数的类型由指令设置。基本类型有WORD(32位有符号)、BIG(64位有符号)、BYTE(8位无符号)、REAL(64位IEEE浮点)和指针(取决于实现)。该指令集遵循CISC处理器的示例,为算术、数据移动等提供三个操作数的内存到内存操作。它还包含分配内存、加载模块以及创建、同步和进程间通信的指令。模块是动态加载的代码和数据的单元。模块由VM指令加载,该指令返回指向模块的方法表的指针。该指针由VM的垃圾收集器管理,因此模块的代码和数据像任何其他内存一样被垃圾收集。通过在模块加载时使用类型的MD5签名检查方法类型,可以保护类型安全。内存管理与虚拟机的指令集紧密相关。DIS使用混合垃圾收集方案:大多数垃圾通过简单的引用计数来收集,而实时着色收集器收集循环数据。因为引用计数是一种精确的垃圾收集形式,而不是保守的形式,所以VM运行时系统必须知道所有数据项的类型。因此,语言到VM编译器为所有复合类型生成类型描述符。此描述符会报告类型中所有指针的位置,从而允许VM在复制项时跟踪引用。内存控制着小型系统的成本,因此虚拟机的设计应尽可能保持较低的内存使用量。通过引用计数垃圾收集,Dis在内存闲置时回收内存。引用计数还消除了高效标记和清除收集所需的大舞台需求。这两个结果都降低了VM及其应用程序的内存需求。与Java VM相比,Java VM的指令集使得在复制对象时很难跟踪引用。这不利于引用计数,因此JVM实现选择了更懒惰的技术,如标记和清除、导致更大的竞技场和延迟收集,这两者都会增加内存使用,从而增加整个系统的成本。解释基于堆栈的虚拟机(SM)(如Java虚拟机(JVM))的各个指令很容易,因为大多数操作数都是隐式的。然而,解释器的高级语言实现比诸如Dis的存储器传送机(MM)中的等效指令集产生更多的存储器通信量。考虑到执行SM的代码将通过这样的代码突发来执行此操作,我们使用L表示加载,S表示s来注释它的内存流量