第二七节课-启动优化

1. 启动时间检测

这节课主要针对的是:

  • 冷启动:在内存中不包含相关的数据,必须从磁盘载入到内存中,这是由系统决定的,但是当APP的内存被覆盖的时候,APP也能决定

对应的热启动则是:杀掉进程以后,数据仍然在,启动APP的时候不用重新加载数据

  • 测试APP启动以 main 函数为分界线,main 函数之前的监测一定要靠系统反馈,main 函数以后的则可以监测,这节课主要针对 main 函数之前的优化,因为 main 函数之后的操作是不统一的,根据业务来写的。

如果想要了解 main 函数之前的耗时,是可以通过以下配置来监测,iOS APP的启动流程可以通过在xcode的Edit Scheme ->Run->Arguments -> Environment Variables中加入环境变量DYLD_PRINT_STATISTICS打印出来:

配置.png

运行APP,打印结果如下:

Total pre-main time:  31.70 milliseconds (100.0%)
         dylib loading time:  27.29 milliseconds (86.0%)
        rebase/binding time: 411015771.5 seconds (61552300.3%)
            ObjC setup time:  78.12 milliseconds (246.4%)
           initializer time:  74.97 milliseconds (236.4%)
           slowest intializers :
             libSystem.B.dylib :   6.10 milliseconds (19.2%)
   libBacktraceRecording.dylib :   7.03 milliseconds (22.1%)
               libobjc.A.dylib :   1.69 milliseconds (5.3%)
    libMainThreadChecker.dylib :  57.82 milliseconds (182.3%)

从结果可以看出四个阶段的名称以及它们对应的耗时。

dylib loading 阶段是动态链接库dylib加载动态库的阶段,包括系统动态库和我们自己的动态库。
rebase/binding这个阶段实际就是两个操作rebase和binding。rebase就是内部符号偏移修正,binding是外部符号绑定。
ObjC setup这一阶段主要是OC类相关的事务,比如类的注册,category、protocol的读取等等。
intializers 程序的初始化,包括所依赖的动态库的初始化。在这期间会调用 Objc 类的 + load 函数,调用 C++ 中带有constructor 标记的函数等。

上面打印出的这四个阶段还是比较粗略的,实际上dyld在启动过程中是比较复杂的。如果将上面的DYLD_PRINT_STATISTICS换成DYLD_PRINT_STATISTICS_DETAILS,打印得会更加详细:

total time: 1.3 seconds (100.0%)
  total images loaded:  398 (392 from dyld shared cache)
  total segments mapped: 21, into 415 pages
  total images loading time: 944.18 milliseconds (68.5%)
  total load time in ObjC:  80.88 milliseconds (5.8%)
  total debugger pause time: 801.87 milliseconds (58.2%)
  total dtrace DOF registration time:   0.00 milliseconds (0.0%)
  total rebase fixups:  16,279
  total rebase fixups time:   7.47 milliseconds (0.5%)
  total binding fixups: 567,346

  total binding fixups time: 275.57 milliseconds (20.0%)
  total weak binding fixups time:   0.03 milliseconds (0.0%)
  total redo shared cached bindings time: 459.30 milliseconds (33.3%)
  total bindings lazily fixed up: 0 of 0
  total time in initializers and ObjC +load:  68.67 milliseconds (4.9%)
                         libSystem.B.dylib :   6.04 milliseconds (0.4%)
               libBacktraceRecording.dylib :   7.01 milliseconds (0.5%)
                libMainThreadChecker.dylib :  52.05 milliseconds (3.7%)
total symbol trie searches:    1378030
total symbol table binary searches:    0
total images defining weak symbols:  50
total images using weak symbols:  110

main 函数之后的优化有几个点:

  • 懒加载
  • 删除不再使用的代码,即便那些代码不在使用,但在它们仍然参与到了编译中,因此也会消耗一部分时间
  • 首屏页面最好不要使用 XibStoryBoard,因为它们本身就需要解析
2. 启动优化:二进制重排

二进制重排原理:虚拟内存和物理内存,早期的计算机只有物理内存,内存条的地址叫做物理地址,早期的数据访问都是通过物理地址来访问的,这样子有个问题就是拿到内存地址就可以对数据进行修改,因此存在数据问题和内存不够用的问题。操作系统发现软件虽然很大,但是实际上用户只使用了部分活跃功能,所以把应用的全部数据载入内存有点浪费,因此虚拟内存就出现了,虚拟内存就是让应用觉得自己被全部载入了内存,实际上并没有,每个应用都有一个虚拟的内存,cpu首先访问虚拟地址,虚拟地址和物理地址会有一个对应

物理内存 & 虚拟内存

物理内存:指的是通过物理内存条获得的内存空间。
虚拟内存:跟物理内存相反,虚拟内存指的一种计算机系统内存管理技术,它使得应用程序认为它拥有连续可用的内存,实际上它通常被分隔成多个物理内存碎片。
只谈概念太空洞,下面我们用图来解释什么是物理内存、什么是虚拟内存。
物理内存的概述图如下:

物理和虚拟内存.png

分析:
在没有虚拟内存的概念之前,每个应用一启动,操作系统就会把整个应用放进物理内存里面

物理内存存在的问题:

  • 内存紧张 - 由于每次都是直接把整个应用放进物理内存里面,很可能出现内存不够用的情况。
  • 进程安全 - 每个进程之间的物理地址是连续的,可以拿到别的进程的地址,容易出现进程不安全的问题。
    虚拟内存的概述图如下:
虚拟内存.png

分析:
虚拟内存的技术出现之后,每个进程并不是直接全部扔进物理内存,而是给每个应用分配一个虚拟的内存,虚拟内存通过虚拟页表来把相应数据放进物理内存里面。

虚拟内存的技术出现之后,也有了内存分页的概念,虚拟页表把一个进程分成若干页,比如:Page1、Page2、Page3......,当启动进程1的时候,只需要把Page1装载进物理内存,以此类推,如上图。
PS:大家注意上图的颜色区分能够很好的理解虚拟内存相关概念。

虚拟内存解决了物理内存存在的两个问题,由于每个进程的数据被分成了很多页装载进内存,不会再出现内存紧张的问题,而且一个应用在物理内存里面的地址是不连续的,所以无法访问到别的进程的地址,也保证了线程安全。
注意:虽然一个应用在物理内存中的内存是不连续的,但是访问数据的时候是连续的,原因就在于我们访问数据访问的是对应的虚拟映射表,这个虚拟映射表记录的某个方法、函数对应在物理内存的真实地址。

缺页中断(PageFault)

在上面我们说过在有了虚拟内存技术之后,内存以分页的形式装载进物理内存里面,比如我们现在启动一个应用只需要先把启动相关的内存Page1到Page10装载进物理内存,当用到A功能的时候发现数据没有在物理内存里面,此时操作系统会阻塞APP进程触发一次PageFault,操作系统会从磁盘中读取相应的数据到物理内存上,再将其映射到虚拟内存页表上面。PageFault耗时非常短,平均在0.5ms左右,所以用户一般感知不到,但是有一种情况可能会造成大量的PageFault,那就是启动时刻,所以我们就可以思考如何减少启动时的PageFault次数,从而优化启动时长。

二进制重排

在分析二进制重排之前,我们先了解一下 Link Map File 是个什么东西。

链接映射文件

链接映射文件:Link Map File,里面记录的是每个类所生成的可执行文件的路径、CPU架构、符号等信息,可以简单的理解为这个文件告诉了我们一个应用的可执行文件的排列顺序。

BuildSetting - Write Link Map File设置为YES。

如下图所示:

linkmap设置.png

编译项目之后根据上图的地址找到我们需要的 Link Map 文件,如图所示:

linkmap文件.png

linkmap 文件内方法的排列顺序遵循两种规则:

  1. 文件的编译顺序,即:先按照项目 - Build Phases - Compile Sources中的顺序排列,如下图所示:
编译顺序.png
  1. 同一个文件内部则遵循从上至下的方法书写顺序排列

这样的排列顺序就导致:启动时刻的代码顺序分别分布在了不同的文件里面,即启动代码并没有在一起,导致启动时刻有大量的 pageFault

优化思路:将所有启动时刻需要调用的方法排列在一起,这就是二进制重排。

缺页中断个数

在进行优化之前,我们首先需要检查一下优化之前的缺页中断个数,打开 Instruments,选择 System Trace,运行之后。分析数据如图,选择“Main Thread”,底部的File Backed Page In即为缺页中断个数。

所下图所示

systemTrace.png

当我们测试过一次后再打开 System Trace,发现缺页中断个数少了很多,这个主要是由于缓存造成的,要想完完整整地去测试冷启动的缺页中断次数的话,可以在杀死app之后再打开几个其他的APP,然后再过个一两分钟之后,再启动这个APP的话,就应该是冷启动了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容