您现在的位置: 万盛学电脑网 >> 智能手机 >> 手机应用 >> 安卓教程 >> 正文

详解 Android 虚拟机 ART 运行时库 分析

作者:佚名    责任编辑:admin    更新时间:2022-06-22

   ART 将会取代Dalvik虚拟机,因为 在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。

  在最新的Google I/O大会上,Google 发布了关于Android上最新的运行时库的情况。这就是Android RunTime (ART). ART 将会取代Dalvik虚拟机,成为Android平台上Java代码的执行工具。虽然自从Android KitKat,就有了一些关于ART的消息,但是基本都是一些新闻性质的,缺乏具体技术细节方面的介绍。本文尝试综合目前已有的各种消息,以及最新放出的 Android L 预览版本的ROM的情况,对ART运行时库做个详细的分析。

详解 Android 虚拟机 ART 运行时库 分析   三联

  和IOS,Windows,Tizen之类的移动平台直接将软件编译成能够直接运行在特定硬件平台上的本地代码不同。Android平台上的软件会被编译器首先编译成通用的“byte-code”,然后再在具体的移动设备上被转换成本地指令执行。

  从Android诞生至今的十几年时间里,Dalvik从开始时非常简单的Java Byte-Code执行虚拟机,逐渐增加各种新的特性,满足应用程序对性能的需求,以及与硬件设备协同演进。这其中包括在Android 2.2版本中引入的即时编译器(JIT-Compiler), 以及随后的多线程支持,以及其他一些优化。

Android平台的演进

  不过,在近两年里,Android整个生态系统的进步对Android虚拟机的需求,目前的Dalvik虚拟机的开发已经无法满足了。Dalvik 最初设计时,处理器的性能很弱,移动设备的内存空间非常有限,而且都是32位的系统。于是Google开始构建一个新的虚拟机来更好的面对未来的发展趋势。这种虚拟机的性能能够在目前的多核处理器,甚至未来的8核处理上轻松扩展,能够满足对大容量存储的支持,以及大容量内存的支持。 于是乎,ART出现了。

  1 架构介绍

APK文件的工作流

  首先,ART的首要设计需求就是完全兼容能在Dalvik上运行的byte-code,即dex(Dalvik executable)。这样的话,对于程序员来说,就不需要对重新编译已有的程序,直接拿APK就可以在Dalvik和ART虚拟机上运行。ART带来的最大的变化,就是使用预编译技术(Ahead-of-Time compile)取代Dalvik中的即时编译技术(Just-In-Time compile)。之前,在应用程序每次执行的时候,虚拟机需要将bytecode编译成本地码执行,而在ART中这种编译操作只需执行一次,随后对该应用程序的执行都可以通过直接执行保存下来的本地码完成。当然,这种预编译技术,需要占用额外的存储空间来存储本地码。正是因为现在移动设备的存储空间越来越大,这种技术才得以应用。

  这种预编译技术使得很多原来无法执行的编译优化技术在新的Android平台上成为可能。因为代码只被编译和优化一次,因此值得花费更多的时间在这次编译上,以便进行更多的优化。Google表示,现在可以在应用程序的整体代码技术上进行更高层次的优化,因为编译器现在能够看到应用程序的整体代码,而之前的即时编译,编译器只能看到并优化应用程序中某个函数或者非常小的一部分代码。采用ART后,代码中异常检查带来的开销绝大部分可以避免,对方法和接口的调用也加快了很多。完成这部分功能的是新添加的“dex2oat”组件,用来替代Dalvik中对应的“dexopt”组件。Dalvik中的 Odex文件(优化后的dex)文件,在ART中也用ELF文件代替了。

  因为ART目前编译生成ELF可执行文件,内核就可以直接对载入内存中的代码进行分页管理,这也会带来更加高效的内存管理,以及更少的内存占用。说到这里,我非常好奇内核中的KSM(Kernel same-page merging)在ART中会有什么样的影响,应该能带来不错的效果吧。我们拭目以待。

  ART对续航时间的影响也是非常显著的。因为不再需要解释执行,JIT也不用在程序运行时工作,这样会直接节省CPU需要执行的指令数,因而耗电降低。

  因为预编译时引入了更多分析和优化,编译的时间会变长,这是ART可能会带来的一个副作用。因此相比Dalvik虚拟机,当设备首次启动及应用程序第一次安装时,需要花费的时间更久。Google声称,这种时间上的增加并不那么恐怖。他们希望并预期日后ART上完成上述动作的时间会和目前的 Dalvik差不多,甚至更短些。

ART与Dalvik性能比较

  上面的图显示,ART带来的性能提升是非常明显的。对于同样的代码,性能提升约2倍左右。Google称,将Android L最终发布的时候,可以预计的性能提升将会像Chessbench一样,有3x的加速。

  2 垃圾收集:理论和实践

  Android虚拟机依赖自动化的内存管理机制,即自动垃圾收集。这一Java语言编程模式的基石也是Android系统自诞生之日起,非常重要的一部分。这里向不太了解垃圾收集概念的朋友解释一下,所谓自动垃圾收集,就是说程序员在编程过程中,不需要自己负责物理内存的存储的分配和释放。只需要使用固定的模式创建你需要的变量或者对象,然后直接利用该变量或对象即可。程序的运行环境会自动在内存中分配相应的内存空间存储该变量或者对象, 并在该变量或者对象失效后,自动释放所分配的内存。这是和其他需要人工进行存储管理的较低层次语言最大的区别。自动垃圾收集的好处是,程序员不必再在编程时担心内存管理的问题,当然,这也是有代价的,那就是程序员无法控制内存何时分配和释放,因而无法在需要时进行优化(Java语言有一些编程接口可以供程序员手工优化程序,但控制方式和粒度有限).

  Android曾经被Dalvik的垃圾收集机制折腾了很久。Android平台的内存普遍较小,每次应用程序需要分配内存,当堆空间(分配给应用程序的一块内存空间)不能提供如此大小的空间时,Dalvik的垃圾收集器就会启动。垃圾收集器会遍历整个堆空间,查看每一个应用程序分配的对象,并对所有可到达的对象(即还会被使用的对象)标记,并将那些没有标记的对象空间释放掉。

  在Dalvik虚拟机中,垃圾收集器执行的过程将导致两次应用程序的停顿:

  一是在遍历堆地址空间阶段,

  另一个是标记阶段。

  所谓停顿,即应用程序所有正在执行的进程将暂停。如果停顿时间过长,将会导致应用程序在渲染时出现丢帧现象,进而导致应用程序的卡顿现象,大大降低用户体验。

  Google声称,在Nexus 5手机上,这种停顿的平均长度在54ms。这个停顿时间将导致平均每次垃圾收集会导致在应用程序渲染显式时丢掉4帧的。

  我自己的经验和测试表明,根据应用程序的不同,停顿的时间可能会增大很多。比如,在官方的FIFA应用程序这一典型程序中,垃圾收集的停顿会非常厉害。

  07-01 15:56:14.275: D/dalvikvm(30615): GCFORALLOC freed 4442K, 25% free 20183K/26856K, paused 24ms, total 24ms

  07-01 15:56:16.785: I/dalvikvm-heap(30615): Grow heap (frag case) to 38.179MB for 8294416-byte allocation

  07-01 15:56:17.225: I/dalvikvm-heap(30615): Grow heap (frag case) to 48.279MB for 7361296-byte allocation

  07-01 15:56:17.625: I/Choreographer(30615): Skipped 35 frames! The application may be doing too much work on its main thread.

  07-01 15:56:19.035: D/dalvikvm(30615): GCCONCURRENT freed 35838K, 43% free 51351K/89052K, paused 3ms+5ms, total 106ms

  07-01 15:56:19.035: D/dalvikvm(30615): WAITFORCONCURRENTGC blocked 96ms

  07-01 15:56:19.815: D/dalvikvm(30615): GCCONCURRENT freed 7078K, 42% free 52464K/89052K, paused 14ms+4ms, total 96ms

  07-01 15:56:19.815: D/dalvikvm(30615): WAITFORCONCURRENTGC blocked 74ms

  07-01 15:56:20.035: I/Choreographer(30615): Skipped 141 frames! The application may be doing too much work on its main thread.

  07-01 15:56:20.275: D/dalvikvm(30615): GCFORALLOC freed 4774K, 45% free 49801K/89052K, paused 168ms, total 168ms

  07-01 15:56:20.295: I/dalvikvm-heap(30615): Grow heap (frag case) to 56.900MB for 4665616-byte allocation

  07-01 15:56:21.315: D/dalvikvm(30615): GCFORALLOC freed 1359K, 42% free 55045K/93612K, paused 95ms, total 95ms

  07-01 15:56:21.965: D/dalvikvm(30615): GCCONCURRENT freed 6376K, 40% free 56861K/93612K, paused 16ms+8ms, total 126ms

  07-01 15:56:21.965: D/dalvikvm(30615): WAITFORCONCURRENTGC blocked 111ms

  07-01 15:56:21.965: D/dalvikvm(30615): WAITFORCONCURRENTGC blocked 97ms

  07-01 15:56:22.085: I/Choreographer(30615): Skipped 38 frames! The application may be doing too much work on its main thread.

  07-01 15:56:22.195: D/