启动优化
2019-06-02 本文已影响0人
affyzh
最近在极客时间学习,做一下总结。
思维导图 启动流程
常见问题:
- 点击图标很久不响应
- 首页显示太慢
- 首页显示后无法操作
启动优化
1. 优化工具
systrace + 函数插桩
通过插桩,我们可以看到应用主线程和其他线程的函数调用流程。其原理,是在函数的入口和出口分别插入以下方法:
class Trace {
public static void in(String tag) {
Trace.beginSection(name);
}
public static void out() {
Trace.endSection();
}
}
class Test {
public void test() {
Trace.i("Test.test()");
// 原来的工作
Trace.o();
}
}
优化方式
- 闪屏优化
- 只在Android6.0或者7.0以上才启用“预览闪屏”(今日头条),让手机性能好的用户可以有更好的体验。
- 合并闪屏和主页面的Activity,减少一个Activity会给线上带来100ms左右的优化(微信)。但这样会导致管理的复杂,特别是来自第三方的启动流程。
- 业务梳理
梳理业务在启动过程中必需的模块,区分那些可以懒加载或者砍掉的模块。同时结合业务场景调整启动模式,如微信的扫一扫启动只加载特定的几个模块即可。对于中低端机器,要学会体验降级,并推动产品经理做一些功能取舍。但是要注意懒加载防止集中化,否则容易出现首页显示后用户无法操作的情形。 - 业务优化
- 优化主线程,最理想是通过算法优化;其次是考虑部分业务异步线程加载,但是需要注意过多的异步线程预加载会让我们的逻辑变得更加复杂。
- 业务优化后期,评估风险在适当的时机解决一些架构和历史包袱带来的问题
- 线程优化
线程优化主要在于减少CPU调度带来的波动,让应用的启动时间更加稳定。
- 根据机器性能控制线程数量,有统一的线程池管理。线程切换的数据从/proc/[pid]/sched文件中可以查看
proc/[pid]/sched:
nr_voluntary_switches:
主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是 IO。
nr_involuntary_switches:
被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占 CPU。
-
检查线程间的锁。通过systrace检查锁等待事件,排查这些等待是否可以优化,特别是防止主线程出现长时间空转。
主线程等待子线程导致空转 - 在使用启动框架(多数使用Pipeline机制,根据业务优先级规定业务初始化时机。如微信的mmkernel,阿里的alpha,它们为各个任务建立依赖关系,最终构成 一个有向无环图。)时,注意配置好业务的依赖关系。
- GC优化
在启动过程中尽量减少GC次数避免主线程长时间的卡顿。对于dalvik,可以使用systrace单独查看整个启动过程GC的时间
python systrace.py dalvik -b 90960 -a com.sample.gc
还可以通过Debug.startAllocCounting()
来监测
// GC 使用的总耗时,单位是毫秒
Debug.getRuntimeStat("art.gc.gc-time");
// 阻塞式 GC 的总耗时
Debug.getRuntimeStat("art.gc.blocking-gc-time");
各个GC事件,参考官方文档《调查RAM使用情况》。如果主线程出现较多的GC同步等待,就需要使用Allocation工具进一步分析。
启动过程避免大量字符串操作,特别是序列化和反序列化。一些频繁创建的对象,如网络库或图片库中的Byte数组,Buffer可以复用,或者考虑移到Native实现。
- 系统调用优化
通过systrace的system service类型,可以看到启动过程System Server的CPU工作情况。在启动过程中尽量不要做系统调用,例如PackageManagerService
操作、Binder
调用等待。
system service
不要过早拉起应用的其他进程,System Server和新的进程都会竞争CPU资源,特别在系统内存不足时,LMK会触发系统杀死和拉起(保活)大量进程,从而影响前台进程的CPU。
一些黑科技优化
- I/O优化 ,启动过程不建议出现网络I/O
-
数据重排
Linux文件系统
Linux文件系统从磁盘读取文件时,会以block为单位去磁盘读取,一般block为4KB。也就是一次性至少读取4KB,然后把4KB的数据放到页缓存Page Cache中。如果下次读取文件数据已在页缓存中,就不会发生真是的磁盘I/O,而是直接从页缓存中读取。
Dex文件用到的类和安装包APK里面各种资源文件一般都比较小,但是读取非常频繁。我们可以利用系统这个机制将他们按照顺序重新排列,减少真实的I/O次数。
- 类重排。ReDex 的interdex可以调整类在Dex中的排列顺序。Classdef在dex是连续存放的,所以可以一次读取一大段,mmap中断缺页就会少一些。
- 资源文件重排
-
类的加载
加载类的过程有个verify class的步骤,它需要校验方法的每一个指令,比较耗时
verify class
我们可以通过hook来去掉verify这个阶段。
启动监控
实验室监控
视频录制。找到启动结束的点有以下方法:
- 80% 绘制。当页面绘制超过 80% 的时候认为是启动完成,不过可能会把闪屏当成启动结束的点,不一定是我们希望的。
- 图像识别。手动输入一张启动结束的图片,当实验系统认为当前截屏页面有80%以上相似度时,就认为是启动结束。这种方法比较灵活,但是实现难度较高。
线上监控
Android Vitals可以对应用冷启动、温启动时间做监控。