基于栈虚拟机vs基于寄存器虚拟机

前言

虚拟机的存在是为了隔绝物理层面上的差异,而在虚拟机中也存在多种执行方式。在介绍Class文件中,我们了解到JVM虚拟机指令执行是通过操作数栈,这样的虚拟机被认为是基于栈的,而Dalvik虚拟机中则是基于寄存器的,那基于栈与基于寄存器的虚拟机到底有什么区别?

首先,我们要先了解栈和寄存器,栈是虚拟机在内存中划分出来的虚拟区域,属于栈帧的一部分。用作数据处理的区域,而寄存器则是一个真实的物理硬件,在cpu旁,不过不同的cpu寄存器数量不同。

基于栈

这里首先我们回顾一下基于栈的虚拟机方法执行的过程。我们用以下代码为例:

1 int a = 33
2 int b = 44
3 int c = a + b

编译出来指令是基于操作数栈的,如下

1 iload_0	//操作数栈读取局部变量的第1个slot
2 iload_1	//操作数栈读取局部变量的第2个slot
3 iadd	//将栈顶的两个slot相加
4 istore_2	//保存到局部变量中第3个slo

以下用图形化来说明指令执行的过程。

《基于栈虚拟机vs基于寄存器虚拟机》

《基于栈虚拟机vs基于寄存器虚拟机》

《基于栈虚拟机vs基于寄存器虚拟机》

从指令层面上,以上一共执行了4步。但其实指令在解释为机器码的过程中,一个指令或许会引起CPU多个操作步骤。如istore,除了将操作数栈顶的值放到局部变量表之外,还执行了出栈的操作,这对cpu来说是两步操作。

我们再从物理层面上来看这个结构:

《基于栈虚拟机vs基于寄存器虚拟机》

可以发现基于栈的虚拟机,除了和cpu交互之外,就没有和物理层面上有挂钩的点。这样的好处在于:对硬件的要求不高,很大程度上消除对硬件的依赖。

同时,操作数栈是先入后出栈,每次对数据的处理都是通过栈顶的出栈入栈,而局部变量表也是一个类数组的机构。因此使得基于栈的虚拟机指令不需要与地址相关,因此也被叫做0地址指令。

基于寄存器

接下来我们再看看基于寄存器的JVM。
一样的代码

1 int a = 33
2 int b = 44
3 int c = a + b

这次编译出来的指令是基于寄存器的

1 add ax bx	//其中AX寄存器的值为33,BX寄存器的值为44,将结果放入AX

这里只产生了1条指令。当了解完基于栈的JVM之后,会很疑惑基于寄存器JVM的实现。

这里我们直接看看他们的物理结构,有助于我们了解背后的实现:

《基于栈虚拟机vs基于寄存器虚拟机》

可以看到基于寄存器的JVM没有了操作数栈的概念,而局部量表中也不会直接存储数值,而是持有寄存器的地址。

我们回头看看上面的指令,将ax,与bx两个寄存器上的值相加后将结果储存在ax。因为没有了操作数栈的概念,只需要直接对寄存器地址进行操作。

基于寄存器的虚拟机不采用在内存中处理暂存数据的方式,而在局部变量表中与寄存器建立映射关系。这样的好处在于:因为不用维护栈的关系,所以使得指令数减少。

这样又会有新的疑问。如果按照图上所示,基于寄存器的虚拟机与硬件挂钩,那还是虚拟机么?

这里给出一点我的理解,虽然局部变量表中虽然有寄存器地址(如ax,bx)。可这个地址绝对不是最终的设备上物理地址。因为不同的设备上,寄存器的架构和数量都存在差异的。因此局部变量表中的地址到物理地址之间还是有进行一定的映射换算的。上图只是为了方便理解而略过了寄存器部分的内容。

基于栈虚拟机vs基于寄存器虚拟机

那么到底两个虚拟机之间到底有什么差异呢?

移植性

基于寄存器的虚拟机虽然采用了寄存器地址换算来达到不与硬件信息挂钩,可对寄存器的数量有一定要求的(寄存器也是超贵的)。而基于栈的虚拟机就不要求寄存器了,因此在可移植性上,基于栈的虚拟机是明显更好的。

指令差异

指令长度:在基于栈的JVM中,一个指令占一个字节。而基于寄存器的指令因为要指定地址,所以所需要的长度也更大,一般占两个字节。

指令数:而基于栈的JVM因为要维护出栈入栈,所以执行同样的操作,需要的指令较多,间接使得执行效率较差。而基于寄存器的则不需要考虑。因此网上也很多人用指令较为紧密来形容基于寄存器的指令,使得执行效率也更高。

总结

基于栈虚拟机vs基于寄存器虚拟机对比
可移植性base 栈 > base register
指令数base 栈 > base register
效率base 栈 < base register
指令长度base 栈 < base register
    原文作者:Ray_Sir_Java
    原文地址: https://blog.csdn.net/Ray_Sir_Java/article/details/121862014
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞