1. Dalvik&ART
1.1 Dalvik
Dalvik是Google公司自己设计用于Android平台的虚拟机。DVM即Dalvik Virtual Machine的缩写,那么DVM和JVM有什么区别呢?
-
DVM基于寄存器,JVM基于栈
寄存器是CPU上面的一块存储空间,栈是内存上面的一段连续的存储空间,所以CPU直接访问自己上面的一块空间的数据的效率肯定要大于访问内存上面的数据。基于栈架构的程序在运行时虚拟机需要频繁的从栈上读取或写入数据,这个过程需要更多的指令分派与内存访问次数,会耗费不少CPU时间,对于像手机设备资源有限的设备来说,这是相当大的一笔开销。DVM基于寄存器架构。数据的访问通过寄存器间直接传递,这样的访问方式比基于栈方式要快很多。
-
执行的字节码文件不一样
DVM执行的是.dex文件,JVM执行的是.class文件。
AVM解释执行的是dex字节码.dex:.java –> .class –> .dex –> .apk
JVM运行的是java字节码.class:.java –> .class –> .jar
-
运行环境的区别
DVM:允许运行多个虚拟机实例,每一个应用启动都运行一个单独的虚拟机,并且运行在一个独立的进程中
JVM:只能运行一个实例,也就是所有应用都运行在同一个JVM中
1.2 ART
ART即Android Runtime,是在Dalvik的基础上做了一些优化。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(JIT, just in time)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT, Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。
ART虚拟机执行的本地机器码:
.java –> java bytecode(.class) –> dalvik bytecode(.dex) –> optimized android runtime machine code(.oat)
1.3 Dalvik和ART区别
Dalvik是运行时解释dex文件,安装比较快,开启应用比较慢,应用占用空间小;ART是安装的时候字节码预编译成机器码存储在本地,执行的时候直接就可以运行的,安装慢,开启应用快,占用空间大;
比较喜欢一个骑自行车的例子,Dalvik好比一个已经折叠起来的自行车,每次骑之前都要先组装才能骑,ART相当于一个已经组装好的自行车,每次直接骑着就走了。
2. dex&odex&oat
2.1 dex
dex(Dalvik Executable),本质上java文件编译后都是字节码,只不过JVM运行的是.class字节码,而DVM运行的是.dex字节码,sdk\build-tools\25.0.2\dx工具负责将Java字节码.class文件转换为Dalvik字节码.dex,dx工具对Java类文件重新排列,消除在类文件中出现的所有冗余信息,避免虚拟机在初始化时出现反复的文件加载与解析过程。一般情况下,Java类文件中包含多个不同的方法签名,如果其他的类文件引用该类文件中的方法,方法签名也会被复制到其类文件中,也就是说,多个不同的类会同时包含相同的方法签名,同样地,大量的字符串常量在多个类文件中也被重复使用。这些冗余信息会直接增加文件的体积,同时也会严重影响虚拟机解析文件的效率。消除其中的冗余信息,重新组合形成一个常量池,所有的类文件共享同一个常量池,由于dx工具对常量池的压缩,使得相同的字符串,常量在DEX文件中只出现一次,从而减小了文件的体积,同时也提高了类的查找速度,此外,dex格式文件增加了新的操作码支持,文件结构也相对简洁,使用等长的指令来提高解析速度。
2.2 odex
odex(Optimized dex),即优化的dex,主要是为了提高DVM的运行速度,在编译打包APK时,Java类会被编译成一个或者多个字节码文件(.class),通过dx工具CLASS文件转换成一个DEX(Dalvik Executable)文件。 通常情况下,我们看到的Android应用程序实际上是一个以.apk为后缀名的压缩文件。我们可以通过压缩工具对apk进行解压,解压出来的内容中有一个名为classes.dex的文件。那么我们首次开机的时候系统需要将其从apk中解压出来保存在data/app目录中。 如果当前运行在Dalvik虚拟机下,Dalvik会对classes.dex进行一次“翻译”,“翻译”的过程也就是守护进程installd的函数dexopt来对dex字节码进行优化,实际上也就是由dex文件生成odex文件,最终odex文件被保存在手机的VM缓存目录data/dalvik-cache下(注意!这里所生成的odex文件依旧是以dex为后缀名,格式如:system@priv-app@Settings@Settings.apk@classes.dex
)。如果当前运行于ART模式下, ART同样会在首次进入系统的时候调用/system/bin/dexopt(此处应该是dex2oat工具吧)工具来将dex字节码翻译成本地机器码,保存在data/dalvik-cache下。 那么这里需要注意的是,无论是对dex字节码进行优化,还是将dex字节码翻译成本地机器码,最终得到的结果都是保存在相同名称的一个odex文件里面的,但是前者对应的是一个.dex文件(表示这是一个优化过的dex),后者对应的是一个.oat文件。通过这种方式,原来任何通过绝对路径引用了该odex文件的代码就都不需要修改了。 由于在系统首次启动时会对应用进行安装,那么在预置APK比较多的情况下,将会大大增加系统首次启动的时间。
从前面的描述可知,既然无论是DVM还是ART,对DEX的优化结果都是保存在一个相同名称的odex文件,那么如果我们把这两个过程在ROM编译的时候预处理提取Odex文件将会大大优化系统首次启动的时间。具体做法则是在device目录下的/device/huawei/angler/BoardConfig.mk中定义WITH_DEXPREOPT := true,打开这个宏之后,无论是有源码还是无源码的预置apk预编译时都会提取odex文件,不过这里需要注意的是打开WITH_DEXPREOPT 宏之后,预编译时提取Odex会增加一定的空间,预置太多apk,会导致system.img 过大,而编译不过。遇到这种情况可以通过删除apk中的dex文件、调大system.img的大小限制,或在预编译时跳过一些apk的odex提取。
2.3 oat
oat文件是ART的核心,是通过/system/bin/dex2oat 工具生成的,实际上是一个自定义的elf文件,里面包含的都是本地机器指令,通过AOT生成的文件,在系统中的表现形式有OAT、ART、ODEX,其中大部分apk在执行AOT后生成的都是odex文件。但是由dex2oat工具生成的oat文件包含有两个特殊的段oatdata和oatexec,前者包含有用来生成本地机器指令的dex文件内容,后者包含有生成的本地机器指令,进而就可以直接运行。其是通过PMS –> installd –> dex2oat的流程生成的,可以在预编译的时候,也可以在开机apk扫描的过程中或者apk安装过程中生成。
3. JIT&AOT
3.1 JIT
JIT(Just In Time Compiler, 即时编译),与Dalvik虚拟机相关。
JIT在2.2版本提出的,目的是为了提高android的运行速度,一直存活到4.4版本,因为在4.4之后的ROM中,就不存在Dalvik虚拟机了。我们使用Java开发android,在编译打包APK文件时,会经过以下流程:
- Java编译器将应用中所有Java文件编译为class文件
- dx工具将应用编译输出的类文件转换为Dalvik字节码,即dex文件
DVM负责解释dex文件为机器码,如果我们不做处理的话,每次执行代码,都需要Dalvik将java代码由解释器(Interpreter)将每个java指令转译为微处理器指令,并根据转译后的指令先后次序依序执行,一条java指令可能对应多条微处理器指令,这样效率不高。为了解决这个问题,Google在2.2版本添加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。但是使用JIT也不一定加快执行速度,如果大部分代码的执行次数很少,那么编译花费的时间不一定少于执行dex的时间。Google当然也知道这一点,所以JIT不对所有dex代码进行编译,而是只编译执行次数较多的dex为本地机器码。
3.2 AOT
AOT(Ahead Of Time),和ART虚拟机相关。
JIT是运行时编译,这样可以对执行次数频繁的dex代码进行编译和优化,减少以后使用时的翻译时间,虽然可以加快Dalvik运行速度,但是还是有弊病,那就是将dex翻译为本地机器码也要占用时间,所以Google在4.4之后推出了ART,用来替换Dalvik。
在4.4版本上,两种运行时环境共存,可以相互切换,但是在5.0+,Dalvik虚拟机则被彻底的丢弃,全部采用ART。ART的策略与Dalvik不同,在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。之后打开App的时候,不需要额外的翻译工作,直接使用本地机器码运行,因此运行速度提高。
总的来说:
- JIT代表运行时编译策略,也可以理解成一种运行时编译器,是为了加快Dalvik虚拟机解释dex速度提出的一种技术方案,来缓存频繁使用的本地机器码
- AOT可以理解运行前编译策略,ART虚拟机的主要特征就是AOT
4. Android N上的改变
4.1 ART缺点
- dex->oat生成时间太久,进而apk安装时间很久
- dex2oat耗用系统资源太多,特别dex2oat占用cpu和memory
- oat文件过大,rom小的设备data空间会吃紧
- Powerconsumption 增加
- ART不太稳定,在M上crash问题太多,debug不太容易
- oat文件是elf格式,所以加载oat文件时候相关依赖库也很多,间接导致app进程占用Memory的增加