Android 启动优化

优化工具

systrace + 函数插桩 可以看到系统的一些关键信息 比如GC System Server CPU调度
可以通过下面指令查看收集支持的systrace类型 比如Dalvik sched ss app

python systrace.py --list-categories

将下面的两个函数分别插入到每个方法的出口和入口 实现插桩 通过它可以看到主线程和其他线程中函数的调用过程

class Trace {
  public static void i(String tag) {
    Trace.beginSection(name);
  }


  public static void o() {
      Trace.endSection();
  }
}

怎么做?

  • 业务梳理和优化
  • 线程优化
    减少CPU调度带来的波动

1⃣️ 控制线程的数量 要有统一的线程池(根据性能控制线程数量)
线程切换的数据可以通过sched文件查看

proc/[pid]/sched:
  nr_voluntary_switches:     
  主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是 IO。    
  nr_involuntary_switches:   //需要特别关注此值
  被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占 CPU。

2⃣️ 检查线程池间的锁
对于可以并发的任务 可以通过线程池最大程度的提升启动速度 避免出现如下主线程空转2950mm的情况


Android 启动优化_第1张图片
主线程空转.png
  • GC优化
    启动过程尽量减少GC的次数 可以通过systrace单独查看整个启动过程GC的时间
python systrace.py dalvik -b 90960 -a com.sample.gc

监控启动过程中GC的总耗时的情况以及阻塞式同步GC的耗时:

// GC 使用的总耗时,单位是毫秒
Debug.getRuntimeStat("art.gc.gc-time");
// 阻塞式 GC 的总耗时
Debug.getRuntimeStat("art.gc.blocking-gc-time");

如果发现主线程出现比较多的同步GC等待 还需要通过Alloation工具做进一步分析
启动过程应该避免出现大量的字符串操作 尤其是序列化与反序列化 复用频繁操作的对象 Native实现一定需要频繁创建的对象 对象的逃逸也容易引起GC问题 应该保证对象的生命周期足够短 在栈上就销毁
关于对象逃逸:
new 操作产生的对象并不一定分配在堆中 JVM通过对象逃逸分析分析出对象的引用的使用你范围从而判断是否要在堆中分配 没有发生逃逸则分配在栈上 一般此对象当成返回值返回则可以视为逃逸

  • 系统调用优化
    通过systraceSystem Service类型 可以监控System Server 的 CPU 工作情况
    启动过程中尽量不要做系统调用 例如PackageManagerService操作 Binder 调用等待
    启动过程不要过早的拉起其他应用的进程 System Server 和新的进程会争夺CPU资源 可能会触发low memory killer

  • I/O优化
    启动过程不建议出现网络I/O 选择适合的数据结构

  • 类重排
    启动过程加载类可以通过复写ClassLoader得到

class GetClassLoader extends PathClassLoader {
    public Class findClass(String name) {
        // 将 name 记录到文件
        writeToFile(name,"coldstart_classes.txt");
        return super.findClass(name);
    }
}

然后通过 ReDexInterdex调整类在 Dex 中的排列顺序 最后可以利用 010 Editor 查看修改后的效果

  • 资源文件重排
    通过修改Kernel源码 单独编译一个特殊的ROM 目的:
    1⃣️ 统计
    应用启动过程中加载了安装包中哪些资源 比如assets、drawable、layout 得到资源加载的顺序列表
    2⃣️ 度量
    资源顺序重排后 需要确定是否真正生效 比如哪些资源文件加载了 是发生了真实的磁盘I/O 还是命中了Page Cache
    3⃣️ 自动化
  • 类的加载
    加载类的过程的verify class 的步骤 需要校验方法的每一个指令 是一个比较耗时的操作
    Android 启动优化_第2张图片
    加载.png

    可以hook去掉verity步骤 来优化时间
// Dalvik Globals.h
gDvm.classVerifyMode = VERIFY_MODE_NONE;
// Art runtime.cc
verify_ = verifier::VerifyMode::kNone;

总结

我们可以从 业务梳理 线程优化 GC优化 系统调用优化 I/O优化 类重排 资源文件重排 这几个角度着手优化

你可能感兴趣的:(Android 启动优化)