讲述这个故事前需要引入Android Art虚拟机的概念,AOT是区别于JIT的一种技术。下面我们开始这段故事。
预先 (AOT) 编译
ART 引入了预先编译机制,可提高应用的性能。ART 还具有比 Dalvik 更严格的安装时验证。
在安装时,ART 使用设备自带的 dex2oat 工具来编译应用。该实用工具接受 DEX 文件作为输入,并为目标设备生成经过编译的应用可执行文件。该工具应能够顺利编译所有有效的 DEX 文件。但是,一些后处理工具会生成无效文件,Dalvik 可以接受这些文件,但 ART 无法编译这些文件。有关详情,请参阅解决垃圾回收问题。
这里引入一篇文章记录jit、aot两者的区别联系
我们使用 Java/Koltin 开发 Android App,在编译打包生成APK文件时,会有这样一个流程:
Java编译器将Java文件编译为class文件
dx工具将编译输出的类文件转换为dex文件(Android虚拟机不支持class文件)
而Android虚拟机有两种:Dalvik和ART,JIT与AOT是虚拟机为了提高运行效率等采用的不同的编译策略。
Dalvik和JIT
JIT意思是Just In Time Compiler,就是即时编译技术,与Dalvik虚拟机相关。
JIT是干嘛的
JIT在Android2.2到Android4.4版本(7.0版本也有,后文会叙述),JIT的目的是为了提高Android的运行效率。
Dalvik虚拟机可以看做是一个Java虚拟机。在 Android系统初期,每次运行程序的时候,Dalvik负责将dex翻译为机器码交由系统调用。这样有一个缺陷:每次执行代码,都需要Dalvik将操作码代码翻译为机器对应的微处理器指令,然后交给底层系统处理,运行效率很低。
为了提升效率Android在2.2版本中添加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行即时编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。JIT 编译器可以对执行次数频繁的 dex/odex 代码进行编译与优化,将 dex/odex 中的 Dalvik Code(Smali 指令集)翻译成相当精简的 Native Code 去执行,JIT 的引入使得 Dalvik 的性能提升了 3~6 倍。
JIT缺陷
每次启动应用都需要重新编译(没有缓存)
运行时比较耗电,耗电量大
ART和AOT
AOT是指"Ahead Of Time",与"Just In Time"不同,从字面来看是说提前编译。
AOT是干嘛的
JIT是运行时编译,是动态编译,可以对执行次数频繁的dex代码进行编译和优化,减少以后使用时的翻译时间,虽然可以加快Dalvik运行速度,但是有一个很大的问题:将dex翻译为本地机器码也要占用时间。 所以Google在4.4推出了全新的虚拟机运行环境ART(Android RunTime),用来替换Dalvik(4.4上ART和Dalvik共存,用户可以手动选择,5.0 后Dalvik被替换)。
AOT 是静态编译,应用在安装的时候会启动 dex2oat 过程把 dex预编译成 ELF 文件,每次运行程序的时候不用重新编译。 ART 对 Garbage Collection(GC)过程的也进行了改进:
只有一次 GC 暂停(Dalvik 需要两次)
在 GC 保持暂停状态期间并行处理
在清理最近分配的短时对象这种特殊情况中,回收器的总 GC 时间更短
优化了垃圾回收的工效,能够更加及时地进行并行垃圾回收,这使得 GC_FOR_ALLOC 事件在典型用例中极为罕见
压缩 GC 以减少后台内存使用和碎片
AOT的缺陷
应用安装和系统升级之后的应用优化比较耗时(重新编译,把程序代码转换成机器语言)
优化后的文件会占用额外的存储空间(缓存转换结果)
JIT和AOT共存
Android 7.0上,JIT 编译器被再次使用,采用AOT/JIT 混合编译的策略,特点是:
应用在安装的时候dex不会再被编译
App运行时,dex文件先通过解析器被直接执行,热点函数会被识别并被JIT编译后存储在 jit code cache 中并生成profile文件以记录热点函数的信息。
手机进入 IDLE(空闲) 或者 Charging(充电) 状态的时候,系统会扫描 App 目录下的 profile 文件并执行 AOT 过程进行编译。
总结
Dalvik,ART是Android的两种运行环境,也可以叫做Android虚拟机 JIT,AOT是Android虚拟机采用的两种不同的编译策略