性能优化

启动优化

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();
  }
}

优化方式

  1. 闪屏优化
  1. 业务梳理
    梳理业务在启动过程中必需的模块,区分那些可以懒加载或者砍掉的模块。同时结合业务场景调整启动模式,如微信的扫一扫启动只加载特定的几个模块即可。对于中低端机器,要学会体验降级,并推动产品经理做一些功能取舍。但是要注意懒加载防止集中化,否则容易出现首页显示后用户无法操作的情形。
  2. 业务优化
  1. 线程优化
    线程优化主要在于减少CPU调度带来的波动,让应用的启动时间更加稳定。
proc/[pid]/sched:
  nr_voluntary_switches:     
  主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是 IO。    
  nr_involuntary_switches:   
  被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占 CPU。
  1. 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实现。

  1. 系统调用优化
    通过systrace的system service类型,可以看到启动过程System Server的CPU工作情况。在启动过程中尽量不要做系统调用,例如PackageManagerService操作、Binder调用等待。
    system service
    不要过早拉起应用的其他进程,System Server和新的进程都会竞争CPU资源,特别在系统内存不足时,LMK会触发系统杀死和拉起(保活)大量进程,从而影响前台进程的CPU。

一些黑科技优化

  1. I/O优化 ,启动过程不建议出现网络I/O
  2. 数据重排


    Linux文件系统

    Linux文件系统从磁盘读取文件时,会以block为单位去磁盘读取,一般block为4KB。也就是一次性至少读取4KB,然后把4KB的数据放到页缓存Page Cache中。如果下次读取文件数据已在页缓存中,就不会发生真是的磁盘I/O,而是直接从页缓存中读取。
    Dex文件用到的类和安装包APK里面各种资源文件一般都比较小,但是读取非常频繁。我们可以利用系统这个机制将他们按照顺序重新排列,减少真实的I/O次数。

  1. 类的加载
    加载类的过程有个verify class的步骤,它需要校验方法的每一个指令,比较耗时


    verify class

    我们可以通过hook来去掉verify这个阶段。

启动监控

实验室监控

视频录制。找到启动结束的点有以下方法:

线上监控

Android Vitals可以对应用冷启动、温启动时间做监控。

延伸

上一篇下一篇

猜你喜欢

热点阅读