前言
Android 经过不断的版本迭代,运行于 OS 上的虚拟机也经过了一系列变化。从 Android 4.x 时代的 Dalvik,演进到 Android 5.0 之后的 ART.
这都是 Google 在 app 运行速度、省电课题上的探索与平衡。
Android 4.x 时期
在 Android 4.x 时代,使用的是 Dalvik
虚拟机,JIT(Just in Time)
编译策略。程序在运行的时候,通过 JIT
将字节码编译为机器码,然后执行。
这个过程由于要通过 JIT
实时的进行转换,效率会比较低。运行时给用户的体验就是慢。
JIT 策略的优点就是 APP 安装过程快,占用的存储空间少。在 Android 4.x
时期,硬件配置还没跟上,可能也是Android
选择 JIT 的一个原因吧。
Dalvik 时期,从 .java 代码到 Dalvik 虚拟机能够识别 .dex 文件的处理过程如下:
将 Dalvik ByteCode 转为 Native Code 的逻辑,在 DVM 内部通过 JIT 进行。
Android 5.0/5.1/6.0 时期
在时,系统默认使用的是 ART 虚拟机。这时的 ART 使用的是 AOT(Ahead of time) 策略,在 APP 安装的时,通过 dex2oat 工具将 DEX 字节码转为 .oat 文件,即 Native Code.
这样,下次启动 APP 时,ART 虚拟机直接运行 Native Code,效率会很高。启动速度刚刚滴。
但这也有缺点,就是 APP 安装过程较长。安装过程中生成额外的 Native Code 也需要存储空间,也就更费存储了。
ART 时期,从 .java 文件到ART 虚拟机运行的 .oat 文件(Native Code),过程如下:
从上图可知,对比 Dalvik 多了一步 dex2oat 的转换,这个过程大大提高了程序的执行效率,当然还跟 ART 虚拟机可以运行 Native Code 有关。
Android 7.0/7.1 时期
Android 7.0,JIT 编译回归,形成 AOT/JIT 混合编译模式,这种混合编译模式的特点是:
- 应用在安装的时候 dex 不会被编译
- 应用在运行时 dex 文件先通过解析器(Interpreter)后会被直接执行,与此同时,热点函数(Hot Code)会被识别并被 JIT 编译后存储在 jit code cache 中并生成 profile 文件以记录热点函数的信息。
- 手机进入 IDLE(空闲) 或者 Charging(充电) 状态的时候,系统会扫描 App 目录下的 profile 文件并执行 AOT 过程进行编译。
可以看出,混合编译模式综合了 AOT 和 JIT 的优点,使得应用在安装速度加快的同时,运行速度、存储空间和耗电量等指标都得到了优化。
有一个缺点,应用在前几次运行时,由于还未进行 AOT 编译, Native 代码还没有生成,所以前几次运行会比较慢。等 Native 代码生成后,运行效率就会变快了。
总结
ART 虚拟机还在不断演进,跟随着硬件配置的提升,后续的策略会有取舍。目前 Android 7.0 上的策略还有提升空间。后续还要持续关注 Android 8.0, 9.0 等版本的策略更新。
参考文献
Closer Look At Android Runtime: DVM vs ART
Android 7.0对ART有什么改进?