了解安卓虚拟机:ART和DVM
Android每个APP都运行在相互独立的虚拟机中,不会因为一个应用的奔溃引起其它正在运行的应用也奔溃。Android虚拟机和Java虚拟机有千丝万缕的联系,Android发展史上一共出现过两种虚拟机:DVM和ART。虚拟机的实现非常复杂,不亚于一个小型OS。不需要刨根问底了解虚拟机实现的每一个细节,但是应用开发需要对底层的虚拟机有一个基本的了解。
Android虚拟机在系统架构中的位置,仅次于Frameworks层之下。参考: 平台架构">Android 开发者 > 平台架构

1、Android和Java虚拟机
安卓虚拟机和Java虚拟机有千丝万缕的联系,但是两者又有很多不同。安卓系统一般运行在便携设备上,性能无法与PC相比,所以安卓虚拟机对架构进行了调整,以此适应性能较低的移动设备。安卓虚拟机并不是JVM,因为它没有遵循JVM规范。
两者的执行文件不同,Android虚拟机最终执行的是APK中的.dex文件。
Java虚拟机:.java -> .class -> .jar -> JVM
Android虚拟机:.java -> .class -> .dex -> ART/DVM
2、DVM
Dalvik虚拟机(Dalvik Virtual Machine)是Google专门为Android编写的虚拟机,Dalvik Virtual Machine的作者是丹伯恩斯坦(Dan Bornstein),Dalvik一词来源于他祖先的故乡。
2.1、历史
随Android诞生,在Android 2.2上DVM开始采用JIT及时编译。直到Android 5.0开始系统默认使用ART虚拟机,DVM才完全退出历史。
2.2、源码
DVM源码可以在AOSP开源项目中查看,在/dalvik 目录下。下面列出部分目录:
docs:文档
dexgen:-
tools:DVM工具
libdex:dex相关的库
dx:dx工具,将java转成dex
dexdump:dex反汇编
opcode-gen:已定义的操作码和Dalvik的指令格式
2.3、堆
DVM运行时堆主要由两部分:Zygote Space和Allocation Space组成。
2.4、垃圾回收
DVM采用的是标记-清除(Mark-Sweep)垃圾回收算法进行GC。
2.5、日志
虚拟机GC额的日志可以通过logcat查看,DVM的GC日志格式如下:(找不到DVM的机器抓取log就不演示了)
<GC原因><释放的对象数量>(释放内存大小)<释放的大对象数量>(释放大对象内存大小) <堆的空闲百分比><已用内存><总内存><暂停时长>
3、ART
ART(Android Runtime)虚拟机是Android 4.4 发布的,替换DVM虚拟机,但是Android 4.4默认采用的还是DVM。从Android 5.0开始默认采用ART,DVM被彻底淘汰。详细ART虚拟机说明参考: 平台 > Android Runtime">Android 开发者 > 平台 > Android Runtime
对于运行 Android 5.0(API 级别 21)或更高版本的设备,每个应用都在其自己的进程中运行,并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。
3.1、历史
在Android 4.4的时候推出,替代DVM。从Android 5.0开始成为安卓系统默认的虚拟机,至今。
3.2、源码
ART源码也随着AOSP一起开源,ART虚拟机源码在/art 目录下。有如下目录:
benchmark/ compiler/ dexdump/ disassembler/ profman/ sigchainlib/ cmdline/ dex2oat/ dexlayout/ libart_fake/ patchoat/ runtime/ test/ tools/
3.3、堆
ART运行时堆主要由四部分组成:Zygote Space、Allocation Space、Image Space、Large Object Space。前两个和DVM堆作用一样,Image Space堆空间用来存放预加载类,Large Object Space堆用来分配给大对象(12kb)。
另外,Zygote Space和Image Space是进程间共享的,这样一个应用加载过的类,其它应用无需重复加载,提高移动设备的内存利用率和运行效率。
3.4、垃圾回收
DVM只采用了标记-清除这一种垃圾回收算法,ART采用了多种垃圾回收算法进行改进:
①Concurrent Mark Sweep(CMS):CMS收集器采用了标记-清除算法实现
②Concurrent Partial Mark Sweep:部分完整的堆垃圾收集器
③Concurrent Sticky Mark Sweep:粘性收集器,基于分代垃圾收集思想
④Marksweep + Semispace:非并发的GC
3.5、log
虚拟机GC额的日志可以通过logcat查看,ART的GC日志格式如下:
<GC原因><垃圾回收器名><释放的对象数量>(释放内存大小)<释放的大对象数量>(释放大对象内存大小) <堆的空闲百分比><已用内存><总内存><暂停时长><GC总耗时>
抓取几个gc日志查看:
I/m.huawei.iawar: Explicit concurrent copying GC freed 18772(1096KB) AllocSpace objects, 11(220KB) LOS objects, 61% free, 3788KB/9932KB, paused 93us total 54.002ms
I/maple: NaiveRC GC for force: async:collected objects: 0(0B), 91.1665% utilization (3MiB/3MiB), max pause: 217us, total pause: 351us, total GC time: 26ms
4、JIT和AOT
安卓虚拟机有两种编译方式:JIT(Just In Time Compiler,即时编译器)和AOT(Ahead Of Time Compiler,预编译)。
4.1、JIT
JIT(Just In Time Compiler),即时编译器。每次安卓虚拟机执行dex文件,都需要先将dex字节码转成机器码,然后由系统执行,很明显这样APP运行效率比较低。于是Android 2.2 DVM就加入了JIT,对多次运行的热点代码进行编译生成机器码,这样对于经常执行的代码速度就比较快。
但是每次应用关闭再打开需要重写进行JIT,预编译的机器码并非持久的。
4.2、AOT
AOT(Ahead Of Time Compiler),预编译。在ART虚拟机中加入了AOT编译方式:将应用字节码全部转换为机器码,这样在运行的时候就直接运行机器码,效率非常高。
但是AOT又有另外几个缺点:应用在安装的时候进行一次AOT,这样的话应用的安装时间就会变慢;其次,字节码转换成机器码占用的存储个多。
Android 7.0 又采用了AOT和JIT混合编译方式:安装的时候并不将字节码转成机器码,在运行的时候将经常运行的字节码转成机器码保存起来。这样安装的时间不会增加,而且后面执行的速度会变快。
5、DVM和ART的区别
DVM和ART的区别有如下,总的来说就是好的淘汰旧的。
①DVM较旧只支持32位CPU,ART支持64位CPU还兼容32位CPU
②DVM只有一种垃圾回收算法,而ART支持多种垃圾回收算器。
③DVM先没有JIT,后来加入JIT。ART先有AOT,后来又加入JIT。
④ART和DVM的堆不同,前面提到。
参考资料:

