DVM大多数实现和传统的JVM相似,但是为了满足Android在手机端内存的限制,Dalvik对JVM做了一些独有的优化。
Dex文件
传统的class文件是由Java源码文件编译生成的,而Android在编译打包的时候,会将所有的class文件整合优化,最终生成class.dex文件。dex文件中去掉了class文件中的冗余信息,使得编译打包之后的class.dex文件更加紧凑,这样在ClassLoader加载解析dex文件的时候减少了I/O操作,提高了类查找的速度。
下面我们新建两个java类文件,分别是Dex1.java和Dex2.java文件:
我们使用javac命令可以将java文件编译成class文件:
javac Dex1.java
javac Dex2.java
使用jar命令将两个class文件打包成一个jar包:
jar cvf AllDex.jar Dex1.class Dex2.class
上面的命令将两个class文件打包成了一个AllDex.jar的包。
最后使用dx命令将jar包优化压缩成class.dex文件:
dx --dex --output AllDex.dex AllDex.jar
打包之后的dex包,可以使用Android SDK中的dexdump工具进行查看:
dexdump -d -l plain AllDex.dex
需要注意的是,Android编译打包对class文件的压缩伴随着一个副作用,就是Android的65535的问题,这一问题最直接的原因就是DVM的源代码MemberIdsSection.java中:
此处规定了一个Dex文件中的方法、属性、类的个数不能超过MAX_MEMBER_IDX(65535),Android为了解决这一问题,提供了MultiDex来解决。
架构基于寄存器/基于栈结构
JVM的字节码指令的执行是基于栈进行的,而Android字节码指令的执行是基于寄存器的,这里的寄存器是在内存中模拟出的一组寄存器,而并非硬件的寄存器。Android和Java的字节码是完全不同的,Android的字节码是二地址或三地址的指令。
我们编写Dex.java文件:
java文件经过javac的编译打包之后的字节码:
使用dx对文件进行优化压缩之后的字节码文件:
Android字节码命令说明:
add-in指令需要三个寄存器参数,这个指令会将后面两个寄存器(v2、v3)相加,然后将结果寄存在第一个寄存器(v0)中;
return指令会将最终的结果返回。
可以看到在class字节码中需要4行的指令,经过Android优化压缩之后只有2行。基于寄存器的指令明显要比基于栈的指令少,虽然指令的长度增加,但是执行的速度得到了提高。
下面是基于栈和基于寄存器指令的对比:
内存管理和回收
DVM和JVM另外一个明显不同的地方就是内存结果的不同,主要体现在堆内存的划分和管理上。DVM中将堆内存分成了两部分:Active Heap和Zygote Heap。
为什么要区分Zygoto Heap和Active Heap
在Android系统中,底层init进程会创建一个Zygoto进程,Zygoto创建了Android中的DVM,其他的应用进程以及SystemService也都是由Zygote进程fork出来的。
Zygote进程在被创建之后,会完成虚拟机的初始化、lib库的加载、系统资源的配置等等,当系统需要创建一个新的应用进程的时候,Zygote通过复制快速fork出一个新的进程,对于一些只读的库和资源,所有的虚拟机实例都是和Zygote共享同一块内存区域,这样大大减小了内存的开销。Android将创建的进程的堆内存划分为两块,其中一块Zygote Heap,就是为了减少相同内容的拷贝,这部分堆内存所有DVM共享,而进程自己的数据读写全部在Active Heap中进行。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有