Java 虚拟机与 Dalvik 虚拟机的区别

来源:互联网 发布:电脑课本软件 编辑:程序博客网 时间:2024/06/08 18:05

简述

JVM基于栈架构
DVM 虚拟机基于寄存器架构(意指由一个指令之输出或输入可以直接索引到的寄存器组群)。
因为实现架构的差异,则DVM对指令的响应要快于 JVM。

代码实践

public class Hello{    public int foo(int a,int b)    {        return (a+b)*(a-b);    }    public static void main(String[] args){        Hello hello = new Hello();        System.out.println(hello.foo(5,3));    }}

编写以上代码保存为Hello.java。打开命令提示符,执行命令javac Hello.java编译生成Hello.class文件。
然后执行命令dx --dex --out=Hello.dex Hello.class
不幸的是,它抛出异常了,看来是 JDK的版本问题造成的。

UNEXPECTED TOP-LEVEL EXCEPTION:java.lang.RuntimeException: Exception parsing classes    at com.android.dx.command.dexer.Main.processClass(Main.java:752)    at com.android.dx.command.dexer.Main.processFileBytes(Main.java:718)    at com.android.dx.command.dexer.Main.access$1200(Main.java:85)    at com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645)    at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:170)    at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)    at com.android.dx.command.dexer.Main.processOne(Main.java:672)    at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574)    at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)    at com.android.dx.command.dexer.Main.run(Main.java:277)    at com.android.dx.command.dexer.Main.main(Main.java:245)    at com.android.dx.command.Main.main(Main.java:106)Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)    at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)    at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)    at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)    at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)    at com.android.dx.command.dexer.Main.parseClass(Main.java:764)    at com.android.dx.command.dexer.Main.access$1500(Main.java:85)    at com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684)    at com.android.dx.command.dexer.Main.processClass(Main.java:749)    ... 11 more1 error; aborting
$ java -versionjava version "1.8.0_45"Java(TM) SE Runtime Environment (build 1.8.0_45-b14)Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

这里需要把JDK版本指定为1.6重新编译Hello.class。(为什么要更换成1.6?)

$ javac -source 1.6 -target 1.6 Hello.java 

然后执行命令dx --dex --out=Hello.dex Hello.class。此刻,已经顺利生成了Hello.dex
接下来执行

    javap -c -classpath . Hello

命令执行后得到如下代码,针对foo()函数:

    // Java 字节码    public int foo(int, int);    Code:       0: iload_1       1: iload_2       2: iadd       3: iload_1       4: iload_2       5: isub       6: imul       7: ireturn

接着执行命令:dexdump可在 sdk/build-tools/目录下找到。

dexdump -d Hello.dex

生成 Dalvik字节码如下,针对foo()函数:

    // Dalvik 字节码    000198:                                        |[000198] Hello.foo:(II)I    0001a8: 9000 0304                              |0000: add-int v0, v3, v4    0001ac: 9101 0304                              |0002: sub-int v1, v3, v4    0001b0: b210                                   |0004: mul-int/2addr v0, v1    0001b2: 0f00                                   |0005: return v0

查看上面的 Java 字节码,共8条指令(每条指令占一个字节,并且这些指令都没有参数)。
Q:那么这些指令是如何存取数据的呢?
A:Java 指令集被称为零地址形式的指令集,所谓零地址形式,是指指令的源参数与目标参数都是隐含的,它通过 Java 虚拟机中提供的一种数据结构“求值栈”来传递的。
结合代码来理解理论知识:

完整指令 PC 类型 指令 连接符 局部变量 步骤说明 0:iload_1 0 i
(int类型) load
(将局部变量存入 Java 栈) - 1
(局部变量索引) 将第2个 int 类型的局部变量进栈 1:iload_2 1 i load - 2 将第3个 int 类型的局部变量进栈 2:iadd 2 i add 从栈顶弹出两个 int 类型值,将值相加,然后把结果压回栈顶 3:iload_1 3 i load - 1 将第2个 int 类型的局部变量进栈 4:iload_2 4 i load - 2 将第3个 int 类型的局部变量进栈 5:isub 5 i sub 从栈顶弹出两个 int 类型值,将值相减,然后把结果压回栈顶 6:imul 6 i mul 从栈顶弹出两个 int 类型值,将值相乘,然后把结果压回栈顶 7:ireturn 7 i return 返回一个 int 值

所以与之类似的还有lloaddloadfload… 更多信息查看:Java字节码。


Java 虚拟机与 Dalvik 虚拟机的区别
相比 Java 字节码,我们依旧对照代码分析 Dalvik 字节码。


比起 JVM字节码显然 Dalvik字节码更简洁,只有4条指令就完成了上面的工作。

第一条指令 add-int 将 v3与 v4寄存器的值相加,然后保存到 v0寄存器。整个指令的操作中使用到了三个参数,v3、v4分别代表 foo()函数的入参。
第二条指令 sub-int 将 v3减去 v4的值保存到 v1寄存器。
第三条指令 mul-int/2addr 将 v0乘以 v1保存到 v0寄存器。
第四条指令返回 v0寄存器的值。

Dalvik虚拟机运行时同样为每个线程维护一个 PC计数器与调用栈,与 Java 虚拟机不同的是,这个调用栈维护一份寄存器列表,寄存器的数量在方法结构体的 registers 字段中给出,Dalvik 迅疾会根据这个值来创建一份虚拟的寄存器列表。

通过分析得出,基于寄存器架构的 Dalvik 虚拟机与基于栈架构的 Java 虚拟机相比,由于生产的代码指令减少了,程序执行的速度会更快一些。

0 0
原创粉丝点击