简述

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 more
1 error; aborting
$ java -version
java 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_10i
(int类型)
load
(将局部变量存入 Java 栈)
-1
(局部变量索引)
将第2个 int 类型的局部变量进栈
1:iload_21iload-2将第3个 int 类型的局部变量进栈
2:iadd2iadd从栈顶弹出两个 int 类型值,将值相加,然后把结果压回栈顶
3:iload_13iload-1将第2个 int 类型的局部变量进栈
4:iload_24iload-2将第3个 int 类型的局部变量进栈
5:isub5isub从栈顶弹出两个 int 类型值,将值相减,然后把结果压回栈顶
6:imul6imul从栈顶弹出两个 int 类型值,将值相乘,然后把结果压回栈顶
7:ireturn7ireturn返回一个 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 虚拟机相比,由于生产的代码指令减少了,程序执行的速度会更快一些。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐