启动优化

作者: affyzh | 来源:发表于2019-06-02 20:49 被阅读0次

    最近在极客时间学习,做一下总结。


    思维导图 启动流程

    常见问题:

    • 点击图标很久不响应
    • 首页显示太慢
    • 首页显示后无法操作

    启动优化

    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. 闪屏优化
    • 只在Android6.0或者7.0以上才启用“预览闪屏”(今日头条),让手机性能好的用户可以有更好的体验。
    • 合并闪屏和主页面的Activity,减少一个Activity会给线上带来100ms左右的优化(微信)。但这样会导致管理的复杂,特别是来自第三方的启动流程。
    1. 业务梳理
      梳理业务在启动过程中必需的模块,区分那些可以懒加载或者砍掉的模块。同时结合业务场景调整启动模式,如微信的扫一扫启动只加载特定的几个模块即可。对于中低端机器,要学会体验降级,并推动产品经理做一些功能取舍。但是要注意懒加载防止集中化,否则容易出现首页显示后用户无法操作的情形。
    2. 业务优化
    • 优化主线程,最理想是通过算法优化;其次是考虑部分业务异步线程加载,但是需要注意过多的异步线程预加载会让我们的逻辑变得更加复杂。
    • 业务优化后期,评估风险在适当的时机解决一些架构和历史包袱带来的问题
    1. 线程优化
      线程优化主要在于减少CPU调度带来的波动,让应用的启动时间更加稳定。
    • 根据机器性能控制线程数量,有统一的线程池管理。线程切换的数据从/proc/[pid]/sched文件中可以查看
    proc/[pid]/sched:
      nr_voluntary_switches:     
      主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是 IO。    
      nr_involuntary_switches:   
      被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占 CPU。
    
    • 检查线程间的锁。通过systrace检查锁等待事件,排查这些等待是否可以优化,特别是防止主线程出现长时间空转。


      主线程等待子线程导致空转
    • 在使用启动框架(多数使用Pipeline机制,根据业务优先级规定业务初始化时机。如微信的mmkernel,阿里的alpha,它们为各个任务建立依赖关系,最终构成 一个有向无环图。)时,注意配置好业务的依赖关系。
    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次数。

    • 类重排。ReDex 的interdex可以调整类在Dex中的排列顺序。Classdef在dex是连续存放的,所以可以一次读取一大段,mmap中断缺页就会少一些。
    • 资源文件重排
    1. 类的加载
      加载类的过程有个verify class的步骤,它需要校验方法的每一个指令,比较耗时


      verify class

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

    启动监控

    实验室监控

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

    • 80% 绘制。当页面绘制超过 80% 的时候认为是启动完成,不过可能会把闪屏当成启动结束的点,不一定是我们希望的。
    • 图像识别。手动输入一张启动结束的图片,当实验系统认为当前截屏页面有80%以上相似度时,就认为是启动结束。这种方法比较灵活,但是实现难度较高。

    线上监控

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

    延伸

    相关文章

      网友评论

        本文标题:启动优化

        本文链接:https://www.haomeiwen.com/subject/drfxxctx.html