Android开机速度
我们知道在L及以后的版本中,当系统升级后,或者恢复出厂设置后,或者第一次开机,其启动时间往往很长,短的有3-4分钟,而长的多达10分钟,那么是原因导致的,我们将分析其原因.
我们知道,在Android的最初的几个版本,Android的app 运行在Dalvik中,完全是解释性执行.在Android2.2中,Google把JVM中常用的技术JIT带进了Dalvik,并且增加了多线程的处理能力.随着岁月的流失,科技的进步,屌丝用户期望的提高,手机厂商拼分数,拼情怀,拼硬件,跟踪赵氏族人的style.终于Android 手机的HW,从singlecore 到了8cores,10 cores. RAM 也从256M,512M, 1G, 2G, 3G 到6G.内部存储也从256M到了128G,256G.Google也顺应潮流,终于在L 中,正式推出ART.
下面是ART 的架构图.
首先ART兼容Dalvik.也就是说ART 能运行”dex”(Dalvik执行文件).因此对Androidapp的开发者来说,他们没有什么区别.两者最大的区别是:ART把JIT(Just-in-Time)变成了AOT(Ahead-of-Time).JIT需要在每次运行app时都需要执行一遍,而AOT 只需要执行一次,而后续再运行此app是不需要再执行,其明显提高了性能.当然ART 这样做,也是有代价的,那就是以空间换时间.ART能对应用的所有code做优化,其把bitcode 编译为ELF文件.而ELF文件也往往比odex文件大很多.而JIT 只能对local/method做优化.ART的另一个缺点是其第一次执行优化时需要更长的时间.这也是导致第一次开机时间过长的原因.
此外在ART中,对GC 也有了很大的提高.我们知道在Dalvik中,GC首先需要”stopall the world”.然后再执行GC.其后果就是影响app的性能.其表现为Janks或者启动时间长,卡顿等.相比较Dalvik的GC,ART 做了如下的提高:
在ART 中有几种不同的GC 类型,分别是MS (Mark-Sweep),CMS(Concurrent Mark-Sweep),SS(Semi-space GC), GSS(Generational Semi-space ), MC(Mark Compact ),其default 是CMS.
在ART 中,有三种GC plan,分别是Sticky GC: 只free 从上次GC 以来新分配的object,这种GC plan 是最常用的,也是最经济的; Partial GC:只free 除zyogte外分配的空间;Full GC,释放APP所有分配的Java空间.其执行时间为sticky GC < partial GC < full GC.ART 会根据heap的memory使用情况,选择GC plan.
在ART的GC 中,只有一次pause 在remark 阶段.而Dalvik中有两次pause, 一次是Root Mark阶段, 另一次是remark 阶段.如下图所示.
ART 对前台应用和后台应用采用不同的GC 算法来来达到性能和memory 的平衡.对于前台应用采用CMS算法, CMS算法不会合并空间, 其好处是反应快,给用户更好的performance体验.但是其可能会导致heap的碎片较多.而对应后台引用采用SS, GSS 和MC 算法, 这些算法可以合并压缩memory空间, 有效的减少heap的碎片并节省memory.
对于ART我们现在有了如下认知,
ART 用AOT(ahead-of-time)进行优化.即在install app的时候,把dex 文件编译为native code 并生成OAT file(依然是odex,但其格式是ELF).
和Dalvik相比,其Performance有了较大的提升并且更节省功耗.
在ART 中,没有code cache(Dalvik 有JIT cache).OAT 文件直接map到memory里.
ART 在zygote 启动的时候, 会把初始化的class, object (boot.art) map 到memory 中去(大概10M 左右).在内存紧张的时候, 系统可以把一些proloaded classes swap out. 这样不但加快了zygote的启动速度,又提升了系统性能.
ART 用dex2oat 工具来进行Dex 文件的本地编译.其编译时间比dexopt要长.
OAT 文件比dexopt 生成的odex 文件要大.
归纳起来就是ART 比Dilvik 性能更好,其把dex文件优化为为本地代码,但是其体积大,编译时间长,运行时的性能好.
由于dex优化为OAT文件的时间比较长,所以第一次开机时间(包括恢复工厂设置/OTA升级后)可能就会比较长如果我们没有预先优化dex文件.所以我们可以用预先优化来规避这个问题, 但是其副作用就是其System image 比较大.在很多屌丝机上, 其内部存储比较小, 这就需要一个平衡.要么忍受第一次的痛苦 (开机时间长), 要么加大投入增大内部存储.
下面我们来看看相关的预先优化选项对ART优化后的OAT文件的大小和系统性能的影响.
我们可以在BoardConfig.mk里定义
WITH_DEXPREOPT := true
这样整个system image 就会被预先优化. 由于在启动时不再需要进行app的dex文件进行优化(dex2oat操作)从而提升其启动速度.
如果我们不想把prebuilts目录中的第三方应用进行预先优化(这些应用在他们的Android.mk文件中有include$(BUILD_PREBUILT) ).而是希望这些app通过playstore 或者app提供商进行升级.我们需要在BoardConfig.mk里进行如下的定义.
WITH_DEXPREOPT :=true
DONT_DEXPREOPT_PREBUILTS := true
这个选项只是把boot image 进行预先优化.简单来说,其主要生成boot.art 和boot.oat.这能显著的减少systemimage 大小.但是,所有的app 都需要在第一次启动的时候进行优化需要花更长的boottime 时间.
我们需要在BoardConfig.mk里进行如下的定义
WITH_DEXPREOPT :=true
WITH_DEXPREOPT_BOOT_IMG_ONLY := true
我们也可以控制单个APP是否需要预先优化.如果我们的APP是通过Google play 来进行升级的,或者为了平衡空间问题,我们可以在app的Android.mk 中设置如下属性.
LOCAL_DEX_PREOPT :=false // 不进行预先优化
LOCAL_DEX_PREOPT :=true // 进行预先优化
我们知道ART 在system 有一份OAT file ,在运行时也会copy 一份到/data/ dalvik-cache下.如果我们内部存储不够,可以enable这个选项.但是这个选项可能会影响运行时的性能.因为ART 会disable和position相关的优化.
在device.mk 中添加,
WITH_DEXPREOPT :=true
WITH_DEXPREOPT_PIC :=true
如果手机没有足够的空间,我们可以enable 这个宏.ART只会预先优化boot class.第一次启动的时间会大大的提升,但是也会大大影响运行时性能.因为其它的appcode 是解释性执行的.
在device.mk中添加,
WITH_ART_SMALL_MODE :=true
在Android 后面的版本中也许会修改成 device.mk:
PRODUCT_PROPERTY_OVERRIDES +=\
dalvik.vm.dex2oat-filter=interpret-only \
dalvik.vm.image-dex2oat-filter=speed
所谓preloaded classes list就是zygote在启动是需要初始化的哪些类.这将会使所有的APPshare zygote启动时初始化的类.从而APP 不需单独去初始化这些类,进而提高了效率.proloaded classes 在frameworks/base/preloaded-classes中定义. 如果我们有自己SDK并且这些类是很多应用都会用到的,我们可以把这些常用的类添加到这个list中.注意如果添加过多无用的类会浪费memory,而没有把APP都会用到的基础类添加进来,也会让每一个app都有一份基础类copy,同样浪费空间.
Image classes 是dex2oat初始化时的预先优化的类保存在boot.art文件中. 这个文件在系统启动时map到memory 中去,这部分memory在memory紧张的情况下可以被swapout. Zygote启动时,可以直接从memory中load这部分image.在 L 中,这部分classes list 和preloaded classes list 相同. 在M 中,可以定制需要添加哪些classess.方法如下,
在device.mk中添加,
PRODUCT_DEX_PREOPT_BOOT_FLAGS += --image-classes=
在L的后续版本,如M 中,可以用compiledclasses list 来预先优化boot classpath 中的某些classes.这对哪些内部存储有限的手机非常有用.dex2oat在做优化时,首先会去check所优化的类是否在$OUT/system/etc/compiled-classeslist 中,如果没有在这个列表中的classes则不会进行优化.只能解释性执行,这样虽然可以节省空间,但是势必影响运行时的性能.
device.mk
PRODUCT_COPY_FILES += :system/etc/compiled-classes
所以如果我们想我们的第一次开机时间比较快,那么请用预先优化吧,在BoardConfig.mk里添加下面这行就可.
WITH_DEXPREOPT := true