Android 既然存在虚拟机, 肯定也是在这个 DVM 上执行的. 它的执行流程和 JVM 很像:
Android
.java 文件 -> .class 文件 -> .dex 文件 ->.apk
DVM 执行的是. dex 格式文件, JVM 执行的是. class 文件, Android 程序编译完之后生产. class 文件, 然后, dex 工具会把. class 文件处理成. dex 文件, 然后把资源文件和. dex 文件等打包成. apk 文件, apk 就是 Android package 的意思.
除了上面所说的, 专利授权的原因除外, 其实还有因为如下原因:
dvm 是基于寄存器的虚拟机, 而 jvm 是基于虚拟栈的虚拟机. 寄存器存取速度比栈快得多, dvm 可以根据硬件实现最大的优化, 比较适合移动设备.
class 文件存在很多的冗余信息, dex 工具会去除冗余信息, 并把所有的. class 文件整合到. dex 文件中, 减少了 I/O 操作, 提高了类的查找速度.
不光是上面这些差异, 还有运行环境.
Dalvik : 一个应用启动都运行一个单独的虚拟机运行在一个单独的进程中
JVM: 只能运行一个实例, 也就是所有应用都运行在同一个 JVM 中
这个是早先的安卓虚拟机, 运行速度还是相当慢的. 基于寄存器的虚拟机允许更快的执行时间, 但代价是编译后的程序更大. 于是新的 Dex 字节码格式 odex 产生了. 它的作用等同于 dex, 只不过是 dex 优化后的格式. 在 App 安装的过程中, 会通过 Socket 向 / system/bin/install 进程发送 dex_opt 的指令, 对 Dex 文件进行优化. 在 DexClassLoader 动态加载 Dex 文件时, 也会进行 Dex 的优化, 形成 odex 文件.
为了适应硬件速度的提升, 随后在 Android 2.2 的 DVM 中加入了 JIT 编译器 (Just-In-Time Compiler).Dalvik 使用 JIT 进行即时编译, 借助 Java HotSpot VM,JIT 编译器可以对执行次数频繁的 dex/odex 代码进行编译与优化, 将 dex/odex 中的 Dalvik Code(Smali 指令集) 翻译成相当精简的 Native Code 去执行, JIT 的引入使得 Dalvik 的性能提升了 3~6 倍.
JIT 编译器的引入, 提升了安装速度, 减少了占用的空间, 但随之带来的问题就是: 多个 dex 加载会非常慢; JIT 中的解释器解释的字节码会带来 CPU 和时间的消耗; 还有热点代码的 Monitor 一直在运行带来电量的损耗.
这种情况下, 手机动不动就卡是难以避免的. 相信各位如果那时候用着 Android 手机, 一定印象非常深刻. 因为并不是那么好用.
这样的状况一直持续到 Andorid 4.4, 带来了全新的虚拟机运行环境 ART(Android RunTime)的预览版和全新的编译策略 AOT(Ahead-of-time). 但那时候. ART 是和 Dalvik 共存的, 用户可以在两者之间进行选择(感觉很奇怪, 作为一个爱好者, 我当时看到这个东西可以切换都是不晓得是什么玩意, 用户可都是小白啊, 没有必要共存的吧). 在 Android 5.0 的时候, ART 全面取代 Dalvik 成为 Android 虚拟机运行环境, 至此. Dalvik 退出历史舞台, AOT 也成为唯一的编译模式.
二, ART
AOT 和 JIT 的不同之处在于: JIT 是在运行时进行编译, 是动态编译, 并且每次运行程序的时候都需要对 odex 重新进行编译; 而 AOT 是静态编译, 应用在安装的时候会启动 dex2oat 通过静态编译的方式, 来将所有的 dex 文件 (包括 Multidex) 编译 oat 文件, 编译完后的 oat 其实是一个标准的 ELF 文件, 只是相对于普通的 ELF 文件多加了 oat data section 以及 oat exec section 这两个段而已.(这两个段里面主要保存了两种信息: Dex 的文件信息以及类信息和 Dex 文件编译之后的机器码). 预编译成 ELF 文件, 每次运行程序的时候不用重新编译, 是真正意义上的本地应用. 运行的文件格式也从 odex 转换成了 oat 格式.
其实在 Android5.0 的时候我们能够明显感觉手机好用很多就是因为这个原因, 从根本上换掉了那种存在着无法解决弊端的虚拟机. 在 Android 5.x 和 6.x 的机器上, 系统每次 OTA 升级完成重启的时候都会有个应用优化的过程, 这个过程就是刚才所说的 dex2oat 过程, 这个过程比较耗时并且会占用额外的存储空间.
AOT 模式的预编译解决了应用启动和运行速度和耗资源 (电等) 问题的同时也带来了另外两个问题:
1, 应用安装和系统升级之后的应用优化比较耗时, 并且会更耗时间. 因为系统和 apk 都是越来越大的.
2, 优化后的文件会占用额外的存储空间
在经过了两个 Android 大版本的稳定后, 在 Android7.0 又再次迎来了 JIT 的 回归.
JIT 的回归, 可不是把 AOT 模式给取代了, 而是形成 了 AOT/JIT 混合编译模式, 这种模式至今仍在使用.
应用在安装的时候 dex 不会被编译.
应用在运行时 dex 文件先通过解析器 (Interpreter) 后会被直接执行 (这一步骤跟 Android 2.2 - Android 4.4 之前的行为一致), 与此同时, 热点函数(Hot Code) 会被识别并被 JIT 编译后存储在 jit code cache 中并生成 profile 文件以记录热点函数的信息.
手机进入 IDLE(空闲) 或者 Charging(充电) 状态的时候, 系统会扫描 App 目录下的 profile 文件并执行 AOT 过程进行编译.
(Profile 文件会在 JIT 运行的过程中生成: 每个 App 都会有自己的 Profile 文件, 保存在 App 本身的 Local Storage 中. Profile 会保存所调用的类以及函数的 Index, 通过 profman 工具进行分析生成)
个人理解: 哪种模式擅长干什么就让他去干什么.
混合编译模式综合了 AOT 和 JIT 的各种优点, 使得应用在安装速度加快的同时, 运行速度, 存储空间和耗电量等指标都得到了优化.
之前一直在说流畅, 真的流畅在 Android7.0 上才感受到了些许. Android7.0 系统也被用了相当长的一段时间. 之后的 Android8.0 和 Android9.0 都是对各方面的优化, 例如编译文件, 编译器, GC..
其中, 值得一提的是华为的方舟编译器.
首先会判断该设备支不支持方舟编译器, 如果支持, 则从应用商店下发方舟版本的包
方舟编译器会把 dex 文件通过自己的 IR 翻译方舟格式的机器码, 据资料说也是一个 ELF 文件, 但是会增加一些段, 猜测是 Dex 中类信息相关的段
通过这种方式, 来消除 Java 与 JNI 之间的通信的损耗, 以及提升运行时的效率
在方舟内部, 还重新完善了 GC 算法, 使得 GC 的频率大大降低, 减少应用卡顿的现象
目前方舟只支持 64 位的 So, 并且对于加壳的 So 会出现一些问题.
方舟编译器适配的应用, 下载手机上都是方舟版本的包, 特制的包用方舟编译器编译效率大大提升, 之后直接执行就可以了, 直接略过了在 ART 虚拟机上预编译的过程. 这样的结果是很完美的, 但是却也没办法跳过一个弊端. 那就是生态. 还是不管是安卓还是 iOS, 这么多年的时间沉淀中, 他们的生态系统早就达到了一个非常完善的地步. 安卓和 iOS 应用已经多达上千万, 而方舟适配应用的数量还非常有限.
谷歌宣布将停止对华为提供安卓系统更新之后, 华为曝光了自主研发的鸿蒙操作系统. 当时网友各种力挺. 不过后来, 华为董事长梁华在谈及鸿蒙系统时称, 鸿蒙系统是为物联网开发的, 用于自动驾驶, 远程医疗等低时延场景. 鸿蒙系统是不是两手准备我们不得而知. 但是, 一个操作系统最重要的就是它的生态环境. 纵观华为现在的整个格局, 目的非常明确, 用方舟编译器来扩大自己的用户群体. 当用户的基数足够庞大时, 可以随时随地建立一个完善的生态系统. 如果在未来某一天, Android 全面限制华为的使用之后, 在这危机关头鸿蒙系统还是很有可能扛起国产手机的一面大旗. 哪怕不是鸿蒙, 我们也需要这样一个生态不是吗?
最初, 突然去了解 Android 中的虚拟机, 一个是想要明白到底 Android 中的虚拟机和 JVM 是不是一回事, 还有就是想要明白华为发布方舟编译器到底快到了哪里.
上述相关资料均来自网络, 侵权必删.
来源: https://www.cnblogs.com/kaspar/p/11934563.html