从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

得到如下指令:从Java入手了解虚拟机(VM)

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

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

可以看出,基于求值栈的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变量与其参与的运算都可以不需要。不经过优化的指令如下:

从Java入手了解虚拟机(VM)指令执行流程如下:从Java入手了解虚拟机(VM)编译期已经确定栈帧的虚拟寄存器的数量,v3 v4是加载数据与运算时使用的寄存器,v0 v1 v2则对面最后三个变量。
数据一量装入寄存器,在尽可能不使用内存的情况下只使用寄存器速度快得多,因为它不用频繁与内存打交道了。

任何事物都有两面性,栈相比寄存器架构它的可移植性更强,栈在任何机器上实现都很容易。在java设计之初就希望它是一个能在所有平台上通吃的语言,所以JVM基于栈。
而寄存器架构的VM往往会把虚拟寄存器与实际的寄存器映射,如果虚拟寄存器的数量小于等于实际的寄存器,则实现起来相对容易,如果虚拟寄存器数量大于了实际的寄存器数量则相对复杂。
Dalvik只用于android平台,性能往往是更需要关注的东西,这样来讲android 4.0x开始基于寄存器的DVM就可以理解了,此时的设备内存普遍高而且CPU的寄存器数量也多。

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

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

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


原文始发于微信公众号(程序猿搬砖):从Java入手了解虚拟机(VM)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/71983.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!