我们知道在Android N 中对其 ART做了比较大的变化。主要是同一程序的代码可能同时运行在本地机器码(编译)、解释和JIT(Just In Time)的混合运行模式,并且不同的用户,同一应用程序的代码,可能运行不同的编译代码。因为有了Profile-guided JIT/AOT Compilation,那么不同的用户行为对同一app可能会有不同的编译结果。N 上做此变化的其目的是为了在安装时间、内存占用、电池消耗和性能之间获得最好的折衷。
ART是在Android KitKat引入并在Lollipop中设为默认的运行方式。ART的主要特征之一就是安装时对应用的AOT编译。这种方式的主要优点就是优化产生的本地代码性能更好,执行起来需要更少的电量。劣势在于安装文件所需的空间和时间。在Lollipop和Marshmallow(译者注:Android 6.0)中,大的应用需要数分钟才能安装完。为了改变这种状态,Android N实现了一个混合模式的运行环境。应用在安装时不做编译,而是解释字节码,所以可以快速启动。ART中有一种新的、更快的解释器,通过一种新的JIT完成,但是这种JIT的信息不是持久化的。取而代之的是,代码在执行期间被分析,分析结果保存起来。然后,当设备idle和充电的时候,ART会执行针对“热代码”进行AOT编译,其他代码不做编译。为了得到更优的代码,ART采用了几种技巧包括深度内联。
对同一个应用可以编译数次,或者找到变“热”的代码路径或者对已经编译的代码进行新的优化,这取决于分析器在随后的执行中的分析数据。
这种混合使用AOT、解释、JIT的策略有如下优点:
JIT architecture
JIT compilation works in this manner:
Android N的编译模式
在compiler_filter.h,我们可以看到dex2oat一共有12种编译模式:
enum Filter {
VerifyNone, // Skip verification but mark all classes as verified anyway.
kVerifyAtRuntime, // Delay verication to runtime, do not compile anything.
kVerifyProfile, // Verify only the classes in the profile, compile only JNI stubs.
kInterpretOnly, // Verify everything, compile only JNI stubs.
kTime, // Compile methods, but minimize compilation time.
kSpaceProfile, // Maximize space savings based on profile.
kSpace, // Maximize space savings.
kBalanced, // Good performance return on compilation investment.
kSpeedProfile, // Maximize runtime performance based on profile.
kSpeed, // Maximize runtime performance.
kEverythingProfile, // Compile everything capable of being compiled based on profile.
kEverything, // Compile everything capable of being compiled.
};
以上12种编译模式按照排列次序逐渐增强,那系统默认采用了哪些编译模式呢?我们可以在在手机上执行getprop | grep pm
查看:
pm.dexopt.ab-ota: [speed-profile] pm.dexopt.bg-dexopt: [speed-profile] pm.dexopt.boot: [verify-profile] pm.dexopt.core-app: [speed] pm.dexopt.first-boot: [interpret-only] pm.dexopt.forced-dexopt: [speed] pm.dexopt.install: [interpret-only] pm.dexopt.nsys-library: [speed] pm.dexopt.shared-apk: [speed]
其含义如下,
install
(应用安装)与first-boot
(应用首次启动)使用的是[interpret-only],即只verify,代码解释执行即不编译任何的机器码,它的性能与Dalvik时完全一致。ab-ota
(系统升级)与bg-dexopt
(后台编译)使用的是[speed-profile],即只根据“热代码”的profile配置来编译。这也是N中混合编译的核心模式。forced-dexopt
,它采用的是[speed]模式,即最大限度的编译机器码,它的表现与之前的AOT编译一致。总的来说,程序使用loaddex动态加载的代码是无法享受混合编译带来的好处,我们应当尽量采用ClassN.dex方式来符合Google的规范。这不仅在ota还是混合编译上,都会带来很大的提升。
Profile-guided compilation
JIT WORK FLOW
dex2oat编译
首先我们来看系统在什么时候会对各个应用做JIT/AOT编译呢?手机在充电+空闲等多个条件下,通过BackgroundDexOptService.java中的JobSchedule下触发编译优化。
js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setPeriodic(TimeUnit.DAYS.toMillis(1))
.build());
dex2oat编译参数
对于[speed-profile]模式,dex2oat编译命令的核心参数如下:
dex2oat --dex-file=./base.apk --oat-file=./base.odex --compiler-filter=speed-profile --app-image-file=./base.art
--profile-file=./primary.prof ...
其流程:若dex2oat参数中有输入profile文件,会读取profile中的数据。与以往不同的是,这里不仅会根据profile文件来生成base.odex文件,同时还会生成称为app_image的base.art文件。
与boot.art类似,base.art文件主要为了加快应用的对“热代码”的加载与缓存。在apk启动时系统加载应用的oat文件以及可能存在的app image文件,app image的作用是记录已经编译好的“热代码”,并且在启动时一次性把它们加载到缓存。预先加载代替用时查找以提升应用的性能。
JIT 参数
dalvik.vm.usejit
- Whether or not the JIT is enabled.dalvik.vm.jitinitialsize
(default 64K) - The initial capacity of the code cache. The code cache will regularly GC and increase if needed. It is possible to view the size of the code cache for your app with:
$ adb shell dumpsys meminfo -d <pid>
dalvik.vm.jitmaxsize
(default 64M) - The maximum capacity of the code cache.dalvik.vm.jitthreshold <integer>
(default 10000) - This is the threshold that the "hotness" counter of a method needs to pass in order for the method to be JIT compiled. The "hotness" counter is a metric internal to the runtime. It includes the number of calls, backward branches & other factors.dalvik.vm.usejitprofiles <true|false>
- Whether or not JIT profiles are enabled; this may be used even if usejit is false.dalvik.vm.jitprithreadweight <integer>
(default todalvik.vm.jitthreshold
/ 20) - The weight of the JIT "samples" (see jitthreshold) for the application UI thread. Use to speed up compilation of methods that directly affect users experience when interacting with the app.dalvik.vm.jittransitionweight <integer>
(dalvik.vm.jitthreshold
/ 10) - The weight of the method invocation that transitions between compile code and interpreter. This helps make sure the methods involved are compiled to minimize transitions (which are expensive).扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有