“ART和Dalvik”的版本间的差异
(以“此页内容翻译自:https://source.android.com/devices/tech/dalvik/ ART,为 Android runtime的简称,它为 Andriod中应用程序和系统服务提供一...”为内容创建页面) |
小 (→经典配置) |
||
(未显示1个用户的9个中间版本) | |||
第1行: | 第1行: | ||
− | + | 此页内容部分翻译自:https://source.android.com/devices/tech/dalvik/ | |
− | ART,为 Android runtime的简称,它为 | + | ART,为 Android runtime的简称,它为 Andriod中应用程序和系统服务提供一种新的运行模式。ART和它的前任 Dalvik都是为 Android专门开发的。ART兼容 Dalvik的文件格式和 Dex文件规范。 |
− | 既然 ART和 Dalvik都兼容 Dex文件规范,所以之前为 Dalvik开发的应用理应在 ART上也能正常运作。不过,仍有一些 Dalvik上的开发技巧在 ART上是不能使用的,可以点击[http://developer.android.com/guide/practices/verifying-apps-art.html 这里] | + | 既然 ART和 Dalvik都兼容 Dex文件规范,所以之前为 Dalvik开发的应用理应在 ART上也能正常运作。不过,仍有一些 Dalvik上的开发技巧在 ART上是不能使用的,可以点击[http://developer.android.com/guide/practices/verifying-apps-art.html 这里]作进一步了解。 |
+ | |||
+ | = ART特性 = | ||
+ | |||
+ | 这里列出的是 ART的一些主要特性: | ||
+ | |||
+ | == AOT(Ahead-of-time)“运行前”编译 == | ||
+ | |||
+ | ART 采用的是“运行前”(AOT)编译,这样使得运行效率得到提升。此外,与 Dalvik相比,ART在应用安装时有着更为严格的校验机制。 | ||
+ | |||
+ | 当应用安装时,ART通过 '''dex2oat'''工具对应用进行编译。这个工具可以把输入的 DEX文件编译生成当前机器兼容的可直接运行的执行机器码。理论上说,这个工具对所有正确的 DEX文件都应该是兼容的,但有些被预处理过的、不正确的 DEX文件,可能会出错(即使之前这些文件可以被 Dalvik相对较宽的校验机制通过)。[http://developer.android.com/guide/practices/verifying-apps-art.html#GC_Migration 更多内容] | ||
+ | |||
+ | == 更优秀的垃圾回收机制 == | ||
+ | |||
+ | 垃圾回收(GC)会影响应用的性能体验,导致界面卡顿、反应迟缓等等。ART在垃圾回收上的提升体现在以下几个方面: | ||
+ | |||
+ | * GC的中断暂停从 Dalvik时的 2次减少为 1次 | ||
+ | |||
+ | * 在 GC暂停时仍可并行处理其他事务 | ||
+ | |||
+ | * 在回收刚创建的、生命周期较短的对象时,消耗的时间更短 | ||
+ | |||
+ | * 更及时有效的回收机制,使得 GC_FOR_ALLOC事件更少发生 | ||
+ | |||
+ | * 更少的内存占用和内存碎片 | ||
+ | |||
+ | == 开发调试提升 == | ||
+ | |||
+ | ART拥有一系列的特性,使得应用的开发和调试更为方便有效。 | ||
+ | |||
+ | === 支持采样分析 === | ||
+ | |||
+ | 在此之前,开发者一般使用 [http://developer.android.com/tools/help/traceview.html Traceview]。Traceview在提供的信息很有用,在 Dalvik中可以细化到到函数调用层面,同时在分析时性能会显著变慢。 | ||
+ | |||
+ | ART支持专用的采样分析,规避了上面这些不足。它可以为应用提供更准确的度量,同时不会出现性能上的显著下降。在 KitKat版本上已经添加了对 Traceview的支持。 | ||
+ | |||
+ | === 支持更多的调试特性 === | ||
+ | |||
+ | ART支持一系统的调试选项,尤其是 监视和 垃圾回收相关方面。比如: | ||
+ | |||
+ | * 查看堆栈中锁的保持者 | ||
+ | |||
+ | * 查看当前某个类的活动对象数量,并查看这个对象及其引用 | ||
+ | |||
+ | * 对特定对象进行事件过滤 | ||
+ | |||
+ | * 查看某一函数的返回值 | ||
+ | |||
+ | * 设置动态断点 | ||
+ | |||
+ | === 更详尽的错误及异常信息 === | ||
+ | |||
+ | 当运行抛出异常时,ART 会提供大量的运行信息。 ART 扩充细化了以下异常:[http://developer.android.com/reference/java/lang/ClassCastException.html java.lang.ClassCastException], [http://developer.android.com/reference/java/lang/ClassNotFoundException.html java.lang.ClassNotFoundException], [http://developer.android.com/reference/java/lang/NullPointerException.html java.lang.NullPointerException]。(ART和最新的 Dalvik一样,为 数组越界和 边界溢出 [http://developer.android.com/reference/java/lang/NullPointerException.html java.lang.NullPointerException]、 [http://developer.android.com/reference/java/lang/NullPointerException.html java.lang.NullPointerException]进行了扩充。) | ||
+ | |||
+ | 例如,[http://developer.android.com/reference/java/lang/NullPointerException.html java.lang.NullPointerException] 异常信息包含这个空指针发生在哪个应用,并且这个应用要尝试操作写入哪个变量,或者在调用哪个函数。下面是一个例子: | ||
+ | <pre> | ||
+ | java.lang.NullPointerException: Attempt to write to field 'int | ||
+ | android.accessibilityservice.AccessibilityServiceInfo.flags' on a null object | ||
+ | reference | ||
+ | </pre> | ||
+ | |||
+ | <pre> | ||
+ | java.lang.NullPointerException: Attempt to invoke virtual method | ||
+ | 'java.lang.String java.lang.Object.toString()' on a null object reference | ||
+ | </pre> | ||
+ | |||
+ | 当应用出现 NE时,ART也能够提供相关信息,包括 Java层和 Native层的堆栈等等。 | ||
+ | |||
+ | = 问题反馈 = | ||
+ | |||
+ | 如果你遇到任何非 JNI类的问题,请通过 AOSP http://b.android.com 反馈。反馈时请包含 "adb bugreport"内容并提供对应 App在 Google Play上的链接;或者,如果可以,请把相关 APK作为附件上传。另外,请注意,反馈内容(包括附件)在网上都是公开的。 | ||
+ | |||
+ | = 目录 = | ||
+ | |||
+ | [[Dalvik位元码]] | ||
+ | |||
+ | = 实用干货 = | ||
+ | == Dalvik和 ART dex文件概述 == | ||
+ | 所有的apk内部包含一个 classes.dex文件,这个文件的 Magic Number(幻数)是: | ||
+ | ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 } | ||
+ | = "dex\n035\0" | ||
+ | |||
+ | |||
+ | 在 Dalvik上,apk包里的 dex文件在安装的时候会通过 dexopt 转化成另一个格式,叫odex(Opitimized dex),存在 /data/dalvik-cache里面,如: | ||
+ | /data/dalvik-cache/data@app@com.wochacha-1.apk@classes.dex | ||
+ | ——文件后缀还是 .dex,命令方式是 apk的存放路径 '/'-> '@' + classes.dex | ||
+ | 同时 Magic Number变成:{ 0x64 0x65 0x79 0x0a 0x30 0x33 0x36 0x00 } = "dey\n036\0" | ||
+ | 这个文件仍不能够直接运行,每次在运行时还要编译生成本地可执行的机器码,因此效率较 ART低 | ||
+ | 这时,可能会疑惑这个文件存在的必要性,实际上这个文件是针对当前机器的硬件对 dex文件进行了定制化,也就是说把这个放到别的设备上,不一定能运行 | ||
+ | 在要编译 rom的时候,如果"WITH_DEXPREOPT=true",会在 /system/app/下同时生成 .apk和 .odex文件(注意,这里后缀又用的 .odex,但实际上和系统在 /data/dalvik-cache/下的 .dex文件是一样的) | ||
+ | |||
+ | |||
+ | 在 ART上,apk包里的 dex文件在安装的时候通过 dex2oat,也会生成一个后缀为 .dex的文件,放在 /data/dalvik-cache中,如: | ||
+ | /data/dalvik-cache/arm/system@app@Bluetooth@Bluetooth.apk@classes.dex | ||
+ | /data/dalvik-cache/arm64/system@vendor@app@ims@ims.apk@classes.dex | ||
+ | ——文件后缀,和命名方式和 Dalvik完全一样,但其实这是完全不同的一个东西了, | ||
+ | 文件格式也完全不同,因为这其实就是一个实打实的 elf文件,Magic Number:{ 0x75, 0x45, 0x4c, 0x46, ...} = " ELF..." | ||
+ | 这个文件已经可以直接在机器上运行 | ||
+ | |||
+ | == 编译选项 == | ||
+ | |||
+ | <font color=#993300>'''WITH_DEXPREOPT'''</font> | ||
+ | 使能编译时生成 OAT,避免第一次开机时编译耗时,但会增大 system分区的空间消耗 | ||
+ | |||
+ | <font color=#993300>'''DONT_DEXPREOPT_PREBUILTS'''</font> | ||
+ | 使能后,将不会对 Android.mk中包含了 include $(BUILD_PREBUILT)的 Apk进行 oat,例如 Gmail,它很可能会在后期通过商店自行升级,而升级后系统中的 oat文件则没有意义了,但又无法删除,会造成空间的浪费(oat比dex文件要大) | ||
+ | |||
+ | <font color=#993300>'''WITH_DEXPREOPT_BOOT_IMG_ONLY'''</font> | ||
+ | 仅仅针对 boot.img进行oat优化(boot.img中包含 boot.art和 boot.oat) | ||
+ | |||
+ | DEX_PREOPT_DEFAULT | ||
+ | <font color=#993300>'''LOCAL_DEX_PREOPT'''</font> | ||
+ | ture|false|nostripping | ||
+ | 可用于各个 Android.mk,对每个 package进行单独配置,当设置为 true时,dex文件将会从 apk中剔除,如果不想剔除可使用 nostripping | ||
+ | |||
+ | <font color=#993300>'''WPRODUCT__DEX_PREOPT_*'''</font> | ||
+ | PRODUCT_DEX_PREOPT_BOOT_FLAGS | ||
+ | 这里的参数将会传至 dex2oat,控制 boot.img的编译优化行为 | ||
+ | PRODUCT_DEX_PREOPT_DEFAULT_FLAGS | ||
+ | 控制除 boot.img外,其他(如 jar, apk)的 OAT编译行为 | ||
+ | 例如: | ||
+ | PRODUCT_DEX_PREOPT_DEFAULT_FLAGS := --compiler-filter=interpret-only | ||
+ | $(call add-product-dex-preopt-module-config,services,--compiler-filter=space) | ||
+ | |||
+ | <font color=#993300>'''WITH_DEXPREOPT_PIC'''</font> | ||
+ | ture|false | ||
+ | 使能 position-independent code,这样在dex2oat编译生成的 odex文件在运行时将不必再从 /system下拷贝到 /data/dalvik-cache/目录下, | ||
+ | 可以节省 /data空间 | ||
+ | |||
+ | <font color=#993300>'''WITH_ART_SMALL_MODE'''</font> | ||
+ | true|false | ||
+ | 设置为 true时,将只编译处于 boot classpath里的类,其他的均不编译,这样既能加快第一次开机时间,因为大部分必要的类已经编译过了; | ||
+ | 同时也能节省不少空间,因为 APP都未进行编译 | ||
+ | 缺点是可能损失一性能,这可能要平时觉察不出,但在跑分软件上会有所体现 | ||
+ | |||
+ | == 经典配置 == | ||
+ | |||
+ | 为了提高第一次开机速度,WITH_DEXPREOPT是必须使能的,这样则在编译阶段会完成 dex2oat的操作,避免在开机时间去做这个转码,节省了开机时间(6min以上缩短2min内)。 | ||
+ | |||
+ | 但会引起一个缺点,那就是 apk中还是包含了 class.dex(dexopt生成的),同时在对应的apk文件夹中又生成了已经转码成oat的 class.odex(dex2oat生成的),相当于这部分重复,造成了大量的空间浪费。 | ||
+ | |||
+ | 为了把 apk包里的 class.dex去除,节省空间,可以打开 DEX_PREOPT_DEFAULT := ture。 | ||
+ | |||
+ | 然而,这样开机速度是快了,而且节省了不少system空间,但开机后,我们会发现即使在 system中已经存在 class.odex的 apk,第一次开机后还是会在 /data下面生成 class.odex,如data/dalvik-cache/arm64/system@app@Music@Music.apk@classes.dex,这是何解?原来 Google为了提高安全性,在每一台机器开机时都会在之前的机器码加一个随机的偏移量,这个偏移量是随机的,每台机器都不相同,而 data分区下的这些文件就是从 system下的 class.odex加上偏移而来。 | ||
+ | <pre class="prettyprint"> | ||
+ | static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { | ||
+ | CHECK_ALIGNED(min_delta, kPageSize); | ||
+ | CHECK_ALIGNED(max_delta, kPageSize); | ||
+ | CHECK_LT(min_delta, max_delta); | ||
+ | |||
+ | std::default_random_engine generator; | ||
+ | generator.seed(NanoTime() * getpid()); | ||
+ | std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta); | ||
+ | int32_t r = distribution(generator); | ||
+ | if (r % 2 == 0) { | ||
+ | r = RoundUp(r, kPageSize); | ||
+ | } else { | ||
+ | r = RoundDown(r, kPageSize); | ||
+ | } | ||
+ | CHECK_LE(min_delta, r); | ||
+ | CHECK_GE(max_delta, r); | ||
+ | CHECK_ALIGNED(r, kPageSize); | ||
+ | return r; | ||
+ | } | ||
+ | </pre> | ||
+ | 所以如果还想节省这一个空间,那我们可以把这个偏移量设定为0。这样,在开机阶段,pm检测到 system下的 class.odex是up-to-date,就不会在 /data下重新生成了。 | ||
+ | |||
+ | 做了以上三个更改后,解决方案堪称完美,但其实还有一个问题,那就是在线升级后第三方的apk的dex2oat还是要做,如果第三方足够多也会占用大量的开机时间,这里要怎么处理?交给大家思考吧! :) |
2015年7月17日 (五) 11:51的最后版本
此页内容部分翻译自:https://source.android.com/devices/tech/dalvik/
ART,为 Android runtime的简称,它为 Andriod中应用程序和系统服务提供一种新的运行模式。ART和它的前任 Dalvik都是为 Android专门开发的。ART兼容 Dalvik的文件格式和 Dex文件规范。
既然 ART和 Dalvik都兼容 Dex文件规范,所以之前为 Dalvik开发的应用理应在 ART上也能正常运作。不过,仍有一些 Dalvik上的开发技巧在 ART上是不能使用的,可以点击这里作进一步了解。
目录 |
ART特性
这里列出的是 ART的一些主要特性:
AOT(Ahead-of-time)“运行前”编译
ART 采用的是“运行前”(AOT)编译,这样使得运行效率得到提升。此外,与 Dalvik相比,ART在应用安装时有着更为严格的校验机制。
当应用安装时,ART通过 dex2oat工具对应用进行编译。这个工具可以把输入的 DEX文件编译生成当前机器兼容的可直接运行的执行机器码。理论上说,这个工具对所有正确的 DEX文件都应该是兼容的,但有些被预处理过的、不正确的 DEX文件,可能会出错(即使之前这些文件可以被 Dalvik相对较宽的校验机制通过)。更多内容
更优秀的垃圾回收机制
垃圾回收(GC)会影响应用的性能体验,导致界面卡顿、反应迟缓等等。ART在垃圾回收上的提升体现在以下几个方面:
- GC的中断暂停从 Dalvik时的 2次减少为 1次
- 在 GC暂停时仍可并行处理其他事务
- 在回收刚创建的、生命周期较短的对象时,消耗的时间更短
- 更及时有效的回收机制,使得 GC_FOR_ALLOC事件更少发生
- 更少的内存占用和内存碎片
开发调试提升
ART拥有一系列的特性,使得应用的开发和调试更为方便有效。
支持采样分析
在此之前,开发者一般使用 Traceview。Traceview在提供的信息很有用,在 Dalvik中可以细化到到函数调用层面,同时在分析时性能会显著变慢。
ART支持专用的采样分析,规避了上面这些不足。它可以为应用提供更准确的度量,同时不会出现性能上的显著下降。在 KitKat版本上已经添加了对 Traceview的支持。
支持更多的调试特性
ART支持一系统的调试选项,尤其是 监视和 垃圾回收相关方面。比如:
- 查看堆栈中锁的保持者
- 查看当前某个类的活动对象数量,并查看这个对象及其引用
- 对特定对象进行事件过滤
- 查看某一函数的返回值
- 设置动态断点
更详尽的错误及异常信息
当运行抛出异常时,ART 会提供大量的运行信息。 ART 扩充细化了以下异常:java.lang.ClassCastException, java.lang.ClassNotFoundException, java.lang.NullPointerException。(ART和最新的 Dalvik一样,为 数组越界和 边界溢出 java.lang.NullPointerException、 java.lang.NullPointerException进行了扩充。)
例如,java.lang.NullPointerException 异常信息包含这个空指针发生在哪个应用,并且这个应用要尝试操作写入哪个变量,或者在调用哪个函数。下面是一个例子:
java.lang.NullPointerException: Attempt to write to field 'int android.accessibilityservice.AccessibilityServiceInfo.flags' on a null object reference
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
当应用出现 NE时,ART也能够提供相关信息,包括 Java层和 Native层的堆栈等等。
问题反馈
如果你遇到任何非 JNI类的问题,请通过 AOSP http://b.android.com 反馈。反馈时请包含 "adb bugreport"内容并提供对应 App在 Google Play上的链接;或者,如果可以,请把相关 APK作为附件上传。另外,请注意,反馈内容(包括附件)在网上都是公开的。
目录
实用干货
Dalvik和 ART dex文件概述
所有的apk内部包含一个 classes.dex文件,这个文件的 Magic Number(幻数)是:
ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
= "dex\n035\0"
在 Dalvik上,apk包里的 dex文件在安装的时候会通过 dexopt 转化成另一个格式,叫odex(Opitimized dex),存在 /data/dalvik-cache里面,如:
/data/dalvik-cache/data@app@com.wochacha-1.apk@classes.dex
——文件后缀还是 .dex,命令方式是 apk的存放路径 '/'-> '@' + classes.dex
同时 Magic Number变成:{ 0x64 0x65 0x79 0x0a 0x30 0x33 0x36 0x00 } = "dey\n036\0"
这个文件仍不能够直接运行,每次在运行时还要编译生成本地可执行的机器码,因此效率较 ART低
这时,可能会疑惑这个文件存在的必要性,实际上这个文件是针对当前机器的硬件对 dex文件进行了定制化,也就是说把这个放到别的设备上,不一定能运行
在要编译 rom的时候,如果"WITH_DEXPREOPT=true",会在 /system/app/下同时生成 .apk和 .odex文件(注意,这里后缀又用的 .odex,但实际上和系统在 /data/dalvik-cache/下的 .dex文件是一样的)
在 ART上,apk包里的 dex文件在安装的时候通过 dex2oat,也会生成一个后缀为 .dex的文件,放在 /data/dalvik-cache中,如:
/data/dalvik-cache/arm/system@app@Bluetooth@Bluetooth.apk@classes.dex
/data/dalvik-cache/arm64/system@vendor@app@ims@ims.apk@classes.dex
——文件后缀,和命名方式和 Dalvik完全一样,但其实这是完全不同的一个东西了,
文件格式也完全不同,因为这其实就是一个实打实的 elf文件,Magic Number:{ 0x75, 0x45, 0x4c, 0x46, ...} = " ELF..."
这个文件已经可以直接在机器上运行
编译选项
WITH_DEXPREOPT
使能编译时生成 OAT,避免第一次开机时编译耗时,但会增大 system分区的空间消耗
DONT_DEXPREOPT_PREBUILTS
使能后,将不会对 Android.mk中包含了 include $(BUILD_PREBUILT)的 Apk进行 oat,例如 Gmail,它很可能会在后期通过商店自行升级,而升级后系统中的 oat文件则没有意义了,但又无法删除,会造成空间的浪费(oat比dex文件要大)
WITH_DEXPREOPT_BOOT_IMG_ONLY
仅仅针对 boot.img进行oat优化(boot.img中包含 boot.art和 boot.oat)
DEX_PREOPT_DEFAULT
LOCAL_DEX_PREOPT
ture|false|nostripping
可用于各个 Android.mk,对每个 package进行单独配置,当设置为 true时,dex文件将会从 apk中剔除,如果不想剔除可使用 nostripping
WPRODUCT__DEX_PREOPT_*
PRODUCT_DEX_PREOPT_BOOT_FLAGS
这里的参数将会传至 dex2oat,控制 boot.img的编译优化行为
PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
控制除 boot.img外,其他(如 jar, apk)的 OAT编译行为
例如:
PRODUCT_DEX_PREOPT_DEFAULT_FLAGS := --compiler-filter=interpret-only
$(call add-product-dex-preopt-module-config,services,--compiler-filter=space)
WITH_DEXPREOPT_PIC
ture|false
使能 position-independent code,这样在dex2oat编译生成的 odex文件在运行时将不必再从 /system下拷贝到 /data/dalvik-cache/目录下,
可以节省 /data空间
WITH_ART_SMALL_MODE
true|false
设置为 true时,将只编译处于 boot classpath里的类,其他的均不编译,这样既能加快第一次开机时间,因为大部分必要的类已经编译过了;
同时也能节省不少空间,因为 APP都未进行编译
缺点是可能损失一性能,这可能要平时觉察不出,但在跑分软件上会有所体现
经典配置
为了提高第一次开机速度,WITH_DEXPREOPT是必须使能的,这样则在编译阶段会完成 dex2oat的操作,避免在开机时间去做这个转码,节省了开机时间(6min以上缩短2min内)。
但会引起一个缺点,那就是 apk中还是包含了 class.dex(dexopt生成的),同时在对应的apk文件夹中又生成了已经转码成oat的 class.odex(dex2oat生成的),相当于这部分重复,造成了大量的空间浪费。
为了把 apk包里的 class.dex去除,节省空间,可以打开 DEX_PREOPT_DEFAULT := ture。
然而,这样开机速度是快了,而且节省了不少system空间,但开机后,我们会发现即使在 system中已经存在 class.odex的 apk,第一次开机后还是会在 /data下面生成 class.odex,如data/dalvik-cache/arm64/system@app@Music@Music.apk@classes.dex,这是何解?原来 Google为了提高安全性,在每一台机器开机时都会在之前的机器码加一个随机的偏移量,这个偏移量是随机的,每台机器都不相同,而 data分区下的这些文件就是从 system下的 class.odex加上偏移而来。
static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { CHECK_ALIGNED(min_delta, kPageSize); CHECK_ALIGNED(max_delta, kPageSize); CHECK_LT(min_delta, max_delta); std::default_random_engine generator; generator.seed(NanoTime() * getpid()); std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta); int32_t r = distribution(generator); if (r % 2 == 0) { r = RoundUp(r, kPageSize); } else { r = RoundDown(r, kPageSize); } CHECK_LE(min_delta, r); CHECK_GE(max_delta, r); CHECK_ALIGNED(r, kPageSize); return r; }
所以如果还想节省这一个空间,那我们可以把这个偏移量设定为0。这样,在开机阶段,pm检测到 system下的 class.odex是up-to-date,就不会在 /data下重新生成了。
做了以上三个更改后,解决方案堪称完美,但其实还有一个问题,那就是在线升级后第三方的apk的dex2oat还是要做,如果第三方足够多也会占用大量的开机时间,这里要怎么处理?交给大家思考吧! :)