【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

   2016-11-03 0
核心提示:之前发在通吧了,发回来供基佬们讨论 原文翻译自Anandtech: http://anandtech.com/print/8231/a-closer-look-at-android-runtime-art-in-android-l 自己翻译。如果有术语错误,请以原文为准。请谅解。深入了解Android L的ART最近的I/O大会之后,google终于正

之前发在通吧了,发回来供基佬们讨论

原文翻译自Anandtech: http://anandtech.com/print/8231/a-closer-look-at-android-runtime-art-in-android-l

自己翻译。如果有术语错误,请以原文为准。请谅解。

深入了解Android L的ART

 【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

最近的I/O大会之后,google终于正式公开了它对Android新的运行环境的计划。Android RunTime,简称ART,用于执行Android上的Java代码的虚拟机,是取代Dalvik的继任者。我们已经在去年秋季上市的KitKat上对它稍有了解,但是对于它的术语和技术细节,Google并未言之过多。

对比其它的移动平台,比如iOS,Windows或者Tizen,它们都运行着针对特定硬件架构编译的本地代码,但绝大多数Android软件都基于通用的代码语言,将编译代码生成的ByteCode转化为设备自身的本机指令。

从最早的Android版本开始,Dalvik从一个不太复杂的虚拟机起步。但是随着时间推移,Google开始意识到让其性能跟上业界硬件脚步的重要性。Google在Android 2.2中加入了JIT编译器,加入了多线程功能,并且想逐步优化改进。

但是,在最近几年中,整个生态系统已经超过了Dalvik的发展过程,所以Google需要建立一个能够为以后打下坚实基础,能够适应今天与未来的八核处理器,大容量存储和大内存的新的虚拟机。

所以ART诞生了。

架构

 【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

首先,ART的设计完全兼容Dalvik已有的字节码格dex(Dalvik executable)。所以从开发者的角度,在从Dalvik转移至ART的过程中,完全无需担心兼容性的问题并且代码无需任何改动。

ART带来最大的改变,就是用AOT(Ahead of time,运行前编译)取代了JIT(Just In Time,运行时编译)。ART将之前需要在每次应用运行时进行的编译,变成了只做一次。在之后所有的执行中,都会执行这一次编译完成的字节码。

当然,这些本地的转换使得应用程序会耗费更多存储空间,并且这个新的方案只有像现在这样Android设备的可用存储空间大幅提升了之后,才成为了可能。

这个转变使得以前很多无法实现的优化成为了可能:因为代码只被优化和编译一遍,所以将它优化到位是很重要的。Google宣称现在已经可以达到一个更高的针对整个程序代码的优化层次,而以前的JIT编译器只能进行本地方法块的优化。一般对代码检测异常的部分被大量的移除,并且方法和接口调用得到了显著的提速。新的"dex2oat"完成以上过程,取代以往Dalvik使用的"dexopt"。ART中也不需要odex文件(optimized dex,优化的dex),被ELF文件取代。

因为ART编译一个ELF可执行文件,所以内核现在可以处理代码分页——这将带来更好的内存管理与更少的内存使用。我很好奇KSM(Kernel same-page merging)会带来什么样的效果,这是绝对值得关注的。

对于电池续航,它的作用也是非常明显的——因为在应用运行时无需进行JIT的工作,直接减少了CPU的使用,降低功耗。

所有这一切带来的唯一坏处,就是一次编译需要更多时间。设备与应用的第一次启动时间,相比Dalvik都会明显变长。Google指出时间不会拉的太长,他们预期最终的正式版甚至会比Dalvik速度更快。

 【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

相比Dalvik,ART的性能提升是显著的。如上图,基本上运行在虚拟机上的代码,都有了2倍的性能提升。

Google指出诸如Chessbench这样提升3倍的应用,更能代表真实情况下,Android L正式版所带来的提升。

垃圾回收(Garbage Collection, GC):理论与实践

Android的虚拟机依靠自动内存管理的模式,它作为Java编程范式的基础从一开始,就是Android的一部分。对自动内存管理这个概念不甚熟悉的人来说,一个比较通俗的解释就是,一个程序员既不手动申请物理内存,也不手动释放它。它不同于以手动管理内存为常态的底层编程。当然,自动管理的好处就是开发者无须担心内存管理问题,坏处就是开发者无法控制,无法自己进行优化。

Android与Dalvik深受Dalvik的GC方案之苦。每次当某一程序需要分配内存并且堆(heap, 一块应用专属的内存空间)无法分配的时候,GC将会启动。

GC的工作是仔细检查堆,枚举所有应用分配的对象,逐一标记仍在使用的对象,并且释放掉剩余的未标记对象。

在Dalvik中,这导致以下两种暂停——一种因枚举过程产生,另一种因标记产生。在这里,暂停指的是为了保证应用的完整性,该应用所有线程所有的代码执行都会停止。如果暂停过长,这将导致在渲染的过程中掉帧,这就是Android卡顿的来源。

Google宣称在Nexus 5上,这样的暂停平均耗时54ms,这导致了每次GC启动时,都会带来至少4帧的掉帧。

在我自己的体验与调查中,这个数字因具体程序不同而差异巨大。比如,官方的FIFA app就是一个十分鲜明的例子,GC进行的十分狂野如下:

07-01 15:56:14.275: D/dalvikvm(30615): GC_FOR_ALLOC 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): GC_CONCURRENT freed 35838K, 43% free 51351K/89052K, paused 3ms+5ms, total 106ms

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

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

07-01 15:56:19.815: D/dalvikvm(30615): WAIT_FOR_CONCURRENT_GC 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): GC_FOR_ALLOC 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): GC_FOR_ALLOC freed 1359K, 42% free 55045K/93612K, paused 95ms, total 95ms

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

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

07-01 15:56:21.965: D/dalvikvm(30615): WAIT_FOR_CONCURRENT_GC 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/dalvikvm(30615): GC_FOR_ALLOC freed 1539K, 40% free 56833K/93612K, paused 87ms, total 87ms

07-01 15:56:22.195: I/dalvikvm-heap(30615): Grow heap (frag case) to 60.588MB for 1331732-byte allocation

07-01 15:56:22.475: D/dalvikvm(30615): GC_FOR_ALLOC freed 308K, 39% free 59497K/96216K, paused 84ms, total 84ms

07-01 15:56:22.815: D/dalvikvm(30615): GC_FOR_ALLOC freed 287K, 38% free 60878K/97516K, paused 95ms, total 95ms

以上是这个程序运行之后几秒内的日志。垃圾回收器一共被唤醒9次,导致程序冻结603ms,总共掉帧214帧。内存分配的请求导致的暂停,在Log中都以“GC_FOR_ALLOC”标记。

而ART承诺的垃圾回收性能的提升是极其巨大的。以下是ART在执行前面同样任务的对比:

07-01 16:00:44.531: I/art(198): Explicit concurrent mark sweep GC freed 700(30KB) AllocSpace objects, 0(0B) LOS objects, 792% free, 18MB/21MB, paused 186us total 12.763ms

07-01 16:00:44.545: I/art(198): Explicit concurrent mark sweep GC freed 7(240B) AllocSpace objects, 0(0B) LOS objects, 792% free, 18MB/21MB, paused 198us total 9.465ms

07-01 16:00:44.554: I/art(198): Explicit concurrent mark sweep GC freed 5(160B) AllocSpace objects, 0(0B) LOS objects, 792% free, 18MB/21MB, paused 224us total 9.045ms

07-01 16:00:44.690: I/art(801): Explicit concurrent mark sweep GC freed 65595(3MB) AllocSpace objects, 9(4MB) LOS objects, 810% free, 38MB/58MB, paused 1.195ms total 87.219ms

07-01 16:00:46.517: I/art(29197): Background partial concurrent mark sweep GC freed 74626(3MB) AllocSpace objects, 39(4MB) LOS objects, 1496% free, 25MB/32MB, paused 4.422ms total 1.371747s

07-01 16:00:48.534: I/Choreographer(29197): Skipped 30 frames! The application may be doing too much work on its main thread.

07-01 16:00:48.566: I/art(29197): Background sticky concurrent mark sweep GC freed 70319(3MB) AllocSpace objects, 59(5MB) LOS objects, 825% free, 49MB/56MB, paused 6.139ms total 52.868ms

07-01 16:00:49.282: I/Choreographer(29197): Skipped 33 frames! The application may be doing too much work on its main thread.

07-01 16:00:49.652: I/art(1287): Heap transition to ProcessStateJankImperceptible took 45.636146ms saved at least 723KB

07-01 16:00:49.660: I/art(1256): Heap transition to ProcessStateJankImperceptible took 52.650677ms saved at least 966KB

ART与Dalvik的差别不能再大——ART在同样的情况下,停顿了12.364ms,前台调用4次GC,后台调用2次。在应用运行的全程中,内存的占用也并无增长,而在Dalvik中,内存的占用增加了4次。掉帧也减少到了63帧。

显然这是一个对于一个烂应用能发生的最坏的场景,在ART的情况下,程序依然会有些许的掉帧,但是坏的编程习惯——比如重载UI线程是Android更需要面对的问题。

ART减轻了以前垃圾回收器的很多负担,使执行过程中的暂停不再必要,第二类暂停也通过在暂停之前尽可能完成工作而极大减少——一种被叫做Packard预清除的技术得以应用,而暂停本身在一个简单的检查与校验的过程中完成。Google宣称他们做到了将暂停时间减小到了3ms,是相对Dalvik GC的极大提升。

 【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

同时,不同于堆的“大对象空间”,Large Object Space,简称LOS,被引入,它依旧在应用程序内存中,它被用于更好的处理大的对象,比如图片。这些大的简单的对象如果使堆变得碎片化,将产生很严重的问题,导致在回收对象时更多的GC调用。在更加智能的内存分配方案下,GC调用频率大幅减少,同时内存的碎片化程度也有显著的降低。

另一个比较好的例子是Hangout的app,在Dalvik中,我们看到这样几次GC调用导致的暂停:

07-01 06:37:13.481: D/dalvikvm(7403): GC_EXPLICIT freed 2315K, 46% free 18483K/34016K, paused 3ms+4ms, total 40ms

07-01 06:37:13.901: D/dalvikvm(9871): GC_CONCURRENT freed 3779K, 22% free 21193K/26856K, paused 3ms+3ms, total 36ms

07-01 06:37:14.041: D/dalvikvm(9871): GC_FOR_ALLOC freed 368K, 21% free 21451K/26856K, paused 25ms, total 25ms

07-01 06:37:14.041: I/dalvikvm-heap(9871): Grow heap (frag case) to 24.907MB for 147472-byte allocation

07-01 06:37:14.071: D/dalvikvm(9871): GC_FOR_ALLOC freed 4K, 20% free 22167K/27596K, paused 25ms, total 25ms

07-01 06:37:14.111: D/dalvikvm(9871): GC_FOR_ALLOC freed 9K, 19% free 23892K/29372K, paused 27ms, total 28ms

我们可以看到所有GC调用的示例,明确的和并发的GC调用是GC通用的清理与维护的调用。for_alloc的调用是内存分配器试图分配内存但是发现并不匹配,于是GC启动以获得更多空间。在中间,我们看到了堆因为碎片化尺寸增加,而且不能装入大对象。总的暂停时间一共90ms。作为对比,以下是Android L预览版中的情况:

07-01 06:35:19.718: I/art(10844): Heap transition to ProcessStateJankPerceptible took 17.989063ms saved at least -138KB

07-01 06:35:24.171: I/art(1256): Heap transition to ProcessStateJankImperceptible took 42.936250ms saved at least 258KB

07-01 06:35:24.806: I/art(801): Explicit concurrent mark sweep GC freed 85790(3MB) AllocSpace objects, 4(10MB) LOS objects, 850% free, 35MB/56MB, paused 961us total 83.110ms

我们不是特别确定这样的记录代表什么,但是我们认为它们是重新为堆指定尺寸的过程。唯一的一次GC调用是在应用完成启动之后,耗时961us。我们没在这次GC之前看到任何类似的调用。比较有趣的事LOS的统计。我们看到LOS中有4个10MB的大对象,以前它们都会被分配在程序的堆中,但现在它们在分配的时候被忽略了,这成功的避免了GC的重复调用与可能拖慢Dalvik速度的碎片化内存。

内存分配系统自身也有提升。当ART自身提供相对Dalvik的25%速度提升时,Google并不是特别满意,并且引入了Linux内核中一个新的内存分配器,替换了当前使用的malloc分配器。

新的分配器叫rosalloc,全称是Rows of slots Allocator,为多线程Java应用程序设计。新的分配器有一个更加细粒度的结构,可以锁定独立的对象。在线程本地区域中的小对象也可以被一起锁定。

这使得分配速度的提升也极其巨大,多达10倍。

垃圾回收算法自身也被修订,用于提升用户体验与避免程序的中断。这些算法尚未完工,而Google最近只是发布了一个新的专用的算法,Moving Garbage collector,主要目的是整理后台程序堆的碎片。

64位支持

ART基于模块化的设计,面向不同的目标架构。因此,它面向目前最常见的架构提供了编译器后端,比如ARM,X86和MIPS,以及ARM64,x86-64和仍未实现的MIPS64。

当我们在iPhone5s的评测中讨论切换至64bit的优劣时,我们已经讨论得很深入了,主要的好处是在完全兼容已有32bit应用的同时,提升了可用的地址空间,提升了性能,以及极大提升的加密解密性能。

Google与Apple的一个很大的区别,至少是在虚拟机应用上,是Google使用了引用压缩避免通常64bit导致的内存膨胀,虚拟机依旧使用了32bit的引用。

 【自己翻译】深入了解 Android L 的 ART from AnandTech_wp7 吧_百度贴吧

Google展示了部分跑分以对比ARM和x86切换至64bit的性能。x86的跑分在Intel的Bay Trail上执行,在不同的RenderScript跑分上展示了2-4.5倍的性能。在ARM这边,相比32bit提升的加密性能由A57/A53系统来展示。它们都不代表真实的使用情况,所以作为性能的预测并无意义。

然而Google也拿出了一些比较有趣的数字。Google内建的Panorama,我们可以看到32bit到64bit有着13-19%的性能提升。我们也可以看到,在64bit下,Cortex-A53的提升更为显著。

Google声称当前Play商店中85%的应用已经准备好立即切换至64bit——这意味着只有15%的应用含有部分本地代码需要开发者进行对64bit架构针对性的编译,在以往切换至64bit的过程中,Google也算是很成功的。

结语

从很多方面来说,Google已经给出了性能的巨大提升,并且解决了很多阻碍Android发展的短板。

ART补足了很多在运行非本地应用与内存管理过程中的缺陷。作为一名开发者,我无法要求更多。以前我必须通过机智的算法与特别的编程技巧解决的诸多性能问题,现在都不再是一个硬伤了。

这也意味着Android终于可以在应用程序的流畅性与性能上,与iOS有所一拼了。这是消费者的大胜利。

Google保证未来依旧会不断改进ART,而且现在的它已经与6个月前刚发布时有了很大的区别。而6个月之后Android L最终上市的时候,它也一定不会与现在完全一致。未来很光明,我已经等不及要看看Google还会拿ART做些什么了。

 
标签: 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 安卓中通知功能的具体实现
    安卓中通知功能的具体实现
    通知[Notification]是Android中比较有特色的功能,当某个应用程序希望给用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知实现。使用通知的步骤1、需要一个NotificationManager来获得NotificationManager manager = (NotificationManager
    02-05 安卓开发
  • Android view系统分析-setContentView
    Android view系统分析-setContentView
    第一天上班,列了一下今年要学习的东西。主要就是深入学习Android相关的系统源代码,夯实基础。对于学习Android系统源代码,也没什么大概,就从我们平常使用最基础的东西学起,也就是从view这个切入点开始学习Android的源码,在没分析源码之前,我们有的时候
    02-05 安卓开发
  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • Android NDK 层发起 HTTP 请求的问题及解决
    Android NDK 层发起 HTTP 请求的问题及解决
    前言新的一年,大家新年快乐~~鸡年大吉!本次给大家带来何老师的最新文章~虽然何老师还在过节,但依然放心不下广大开发者,在此佳节还未结束之际,给大家带来最新的技术分享~ 事件的起因不说了,总之是需要实现一个 NDK 层的网络请求。为了多端适用,还是选择
  • Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
    Android插件化(六): OpenAtlasの改写aapt以防
    引言Android应用程序的编译中,负责资源打包的是aapt,如果不对打包后的资源ID进行控制,就会导致插件中的资源ID冲突。所以,我们需要改写aapt的源码,以达到通过某种方式传递资源ID的Package ID,通过aapt打包时获取到这个Package ID并且应用才插件资源的命名
    02-05 安卓开发
  • Android架构(一)MVP架构在Android中的实践
    Android架构(一)MVP架构在Android中的实践
    为什么要重视程序的架构设计 对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。 这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,
    02-05 安卓开发
  • 安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件 作者: 飞龙 这个教程中我们要分析一个锁机软件。像这种软件都比较简单,完全可以顺着入口看下去,但我这里还是用关键点来定位。首先这个软件的截图是这样,进入这个界面之后,除非退出模拟器,否则没办法回到桌面。上面那
    02-05 安卓开发
  • Android插件化(二):OpenAtlas插件安装过程分析
    Android插件化(二):OpenAtlas插件安装过程分析
    在前一篇博客 Android插件化(一):OpenAtlas架构以及实现原理概要 中,我们对应Android插件化存在的问题,实现原理,以及目前的实现方案进行了简单的叙述。从这篇开始,我们要深入到OpenAtlas的源码中进行插件安装过程的分析。 插件的安装分为3种:宿主启动时立
    02-05 安卓开发
  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用FileProvider解决file:// URI引起的FileUriExposedException
    使用FileProvider解决file:// URI引起的FileUri
    问题以下是一段简单的代码,它调用系统的相机app来拍摄照片:void takePhoto(String cameraPhotoPath) {File cameraPhoto = new File(cameraPhotoPath);Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePhotoIntent.putExtra(Medi
    02-05 安卓开发
点击排行