从Java入手如何了解虚拟机(VM)

描述

「这里的虚拟机不是模拟完整硬件的虚拟机,这里主要了解的是JVM与DVM的架构,它们执行的是字节码。」

虚拟机的设计架构有两种: 基于求值栈、基于寄存器,如果从更大的范围可以认为只要实现了功能的都可以认为是虚拟机,通过直接遍历AST得到结果的也可以算一种虚拟机。

「基于AST」 的虚拟机是通过后序遍历AST节点,利用调用栈递归的对AST求值,它的特点就是实现更简单,利用更低级语言或者自身来实现。

「基于求值堆栈」 的虚拟机是零地址指令设计的一个很好的实践,它的操作数是隐藏在栈顶的,利用了栈的先进后出的特点实现了运算的优先级,相对比寄存器方案他的指令数量更多,但是空间占用更低。

「基于虚拟寄存器」 的虚拟机可以是二地址或者三地址设计,操作数存放在虚拟寄存器中,很多指令只需要对寄存器进行读取,不涉及到速度慢了很多的内存,相对于求值栈的方案,指令数量更少,空间占用更高。

「有图有真相」

下面我们来看一段非常简单的Java代码,直观的感受一下求值栈与寄存器方案的差异。

class Test {
  public static void foo() {  
        int a = 1;  
        int b = 2;  
        int c = (a + b) * 5;  
    } 
}

「求值栈寄存器」

这个程序很简单,使用javac将它编译成字节码,再利用javap查看指令

javac Test.java
javap -v Test

得到如下指令:虚拟机

接下来,通过观察指令执行流程了解JVM基于栈的逻辑:

可以看出,基于求值栈的VM在执行的时候会反复的对栈进行push与pop的操作,这样一来需要执行的指令条数就多了。

当然,上面的指令是未优化的,实际上在生成指令之前可以将a与b直接优化掉(常数折叠),减少内存占用。

「寄存器虚拟机」

同样是执行Java代码, android 的Dalvik VM是采用基于寄存器的架构,通过以下命令:

dx --dex --no-optimize --output Test.dex Test.class

如果不显示的指定--no-optimize,生成的Test.dex经过优化后,foo函数里面所有的变量与运算都被优化掉了,只剩下一个return-void指令,从上下文分析可以得出foo内部的a,b,c变量与其参与的运算都可以不需要。不经过优化的指令如下:

虚拟机指令执行流程如下:虚拟机编译期已经确定栈帧的虚拟寄存器的数量,v3 v4是加载数据与运算时使用的寄存器,v0 v1 v2则对面最后三个变量。

数据一量装入寄存器,在尽可能不使用内存的情况下只使用寄存器速度快得多,因为它不用频繁与内存打交道了。

任何事物都有两面性,栈相比寄存器架构它的可移植性更强,栈在任何机器上实现都很容易。在java设计之初就希望它是一个能在所有平台上通吃的语言,所以JVM基于栈。

而寄存器架构的VM往往会把虚拟寄存器与实际的寄存器映射,如果虚拟寄存器的数量小于等于实际的寄存器,则实现起来相对容易,如果虚拟寄存器数量大于了实际的寄存器数量则相对复杂。

Dalvik只用于android平台,性能往往是更需要关注的东西,这样来讲android 4.0x开始基于寄存器的DVM就可以理解了,此时的设备内存普遍高而且CPU的寄存器数量也多。

「总结:」 栈与寄存器架构各有优劣,任何的事物在设计之初都有它考虑的重点,它们没有绝对的优劣,如果你要用AST来实现运算,只要满足了你的要求,无可厚非。

好比时间与空间在写的程序里永远是一个矛盾的存在,人们总是在追求一个极致的平衡点。

如果你觉得文章对你有帮助,可以分享给更多的人或者点在看

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分