iOS 启动优化(一)-理论篇

相关文献:
启动优化(一)-理论篇
启动优化(二)-二进制重排篇
启动优化(三)-编译期插桩篇
启动优化(四)-生成 Order File

本章节研究的内容:
1.冷启动与热启动
2.分析程序启动的时耗组成
3.虚拟内存
4.rebase/binding

一、冷启动与热启动

冷启动:APP被后台kill后重新启动APP,这种启动方式叫做冷启动。(启动优化一般讲的是冷启动)
热启动:APP的状态由running切换为suspend,APP没有被kill仍然在后台运行。再次把APP切换到前台,这种启动方式叫热启动。

1.启动时间的组成

启动时间的划分可以把main()函数作为关键点分割成两块: pre-mainmain()/main后渲染第一帧

main()函数启动分块.png

t1阶段,main()之前的处理所需时间,称为pre-main

t1阶段.png

t2阶段,main()及main()之后处理所需时间(t2阶段耗时的主要是业务代码)

推荐 BLStopwatch,这个工具可以打点统计业务耗时
把下载的代码拖入工程后,在你想测试性能的地方加入代码(这里测试延时操作)

运行后方法调用的耗时的结果:

业务耗时结果
2.检测启动时间

Xcode 测量 pre-main (t1阶段) 时间

查看启动时长:
Xcode 中提供了测量 pre-main 的时间 Edit scheme -> Run -> Auguments 添加环境变量 DYLD_PRINT_STATISTICSvalue设为YES

启动以后可以看到启动时长
启动时长.png

DYLD_PRINT_STATISTICS_DETAILS
把上面的DYLD_PRINT_STATISTICS环境变量替换成DYLD_PRINT_STATISTICS_DETAILS可以看到更详细的信息

更详细的信息.png

3.分析pre-main启动时长
  • dylib loading time
    dyld动态链接器,把所有的可执行文件所依赖的动态库递归加载到内存中。
    分析每个dylib(大部分是系统的),找到其Mach-O文件,打开并读取验证有效性;找到代码签名注册到内核,最后对dylib的每个segment调用mmap()
    dylib的加载过程中系统为了安全考虑引入了ASLRAddress Space Layout Randomization)技术和代码签名。
    ASLR技术:镜像Image、可执行文件、dylibbundle在加载的时候,会在其指向的地址(preferred_address)前面添加一个随机数偏差(slide),防止应用内部地址被定位。

  • rebase/binding time
    dylib加载完成之后,它们处于相互独立的状态,需要绑定起来;
    rebase将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO;
    binding是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。

  • ObjC setup time
    runtime会维护一张类名与类的方法列表全局表
    读取所有类,将类对象其注册到这个全局表中(class registration);
    读取所有分类,把分类加载到类对象中(category registration);
    检查selector的唯一性(selector uniquing)。

  • initializer time
    各种初始化的操作,⽐如执⾏objc的+load函数C++的构造函数等。不要在
    +load函数⾥⾯做⼀些耗时的操作,或者把⼀些操作延时的放在+initialize⾥⾯去执⾏。

启动pre-main时耗的组成
4.优化思路
  • a.移除不需要用到的动态库,尽量使用系统库,且苹果建议动态库数量控制在 6 个以下(超过6个建议合并),防止劣化,需要严格管控动态库的引入;
    源码形式的是可以通过CocoaPods命令转静态库的,如下:
# CocoaPods 打包静态库 命令 
# 其中 –library 指定打包成.a文件,如果不带上将会打包成.framework文件。–force 是指强制覆盖。 
pod package xxxx.podspec --force 

CocoaPods不使用 use_frameworks! 字段,全部引入静态库。

  • b.移除不需要用到的类;合并功能类似的类和扩展;经测试 20000 个类会增加约 800毫秒
  • c.尽量进行懒加载,尽量避免在load()方法里执行操作,把操作推迟到initialize()方法
  • d.利用好多核多线程,但也要注意控制好线程的数量和优先级;
  • e.可以尝试让代码执行更快。比如,频繁访问的可以只获取一次就存下来。
5.推荐好用的工具
  • 推荐一:检查未被使用过的类

安装工具fui查看工程中有哪些类没有被使用过

$ cd ... // cd到工程根目录
$ fui find
  • 推荐二:检查未被使用过的图片

下载LSUnusedResources并运行,会在你的mac上安装一个app,这个app可以过滤出未被使用过的图片。缩小打包后体积。

LSUnusedResourcesExample.gif

三、物理内存/虚拟内存分析

1.物理内存

在以前的古老操作系统app占用的是使用物理内存,比如说一个app占用4G内存,启用4个app就用完了,而你使用这个app并不是所有的页面所有的功能都会去使用,它就一次性加载了。
不安全:能让A进程访问到B进程的地址
致命缺点:1、内存不足 2、安全问题

2.虚拟内存

内存是分页管理的,映射表不能以字节为单位,是以页为单位。
Linux以4K为一页
macOS以4K为一页
iOS以16K一页

$ pageSize  // 终端输入pageSize,得到结果是4096  
// 返回的就是4 * 1024 = 4096

虚拟内存 : 解决安全问题、解决内存使用率问题

用户使用时并不会使用到全部内存,如果 App 一启动就全部加载到内存中会浪费很多内存空间。 虚拟内存技术 的出现就是为了解决这个内存浪费问题。

App 启动后会认为自己已经获取到整个 App 运行所需的内存空间,但实际上并没有在物理内存上为他申请那么大的空间,只是生成了一张虚拟内存和物理内存关联的表

虚拟内存.png
3.地址翻译

系统会生成一张 虚拟内存->物理内存 的映射表。
当 App 需要使用某一块虚拟内存的地址时,会通过这张表查询该虚拟地址是否已经在物理内存中申请了空间。这个过程交由一个叫MMU的硬件去处理(内存管理单元)

如果已经申请了则通过表的记录访问物理内存地址,
如果没有申请则申请一块物理内存空间并记录在表中(Page Fault)。

这个通过进程映射表映射到不同的物理内存空间的操作叫 地址翻译 ,这个过程需要 CPU 和操作系统配合。

4.Page Fault

当数据未在物理内存会进行下列操作:
1.系统阻塞该进程(终断进程)
2.将磁盘中对应Page的数据加载到内存
3.把虚拟内存指向物理内存(继续进程)
上述行为就就是Page Fault

Page Fault的数量和加载耗时长都会随着代码增加而增加。

$ PAGESIZE // 查看你的系统在一页page的大小 - M1芯片16K、inter芯片4K
Page Fault.png

灵活内存管理
虽然解决了浪费问题,但是万一物理内存空间全都被申请了呢?还是有可能产生内存不足的情况的,为保证当前App的正常使用,数据加载遵循以下原则:
1.如果有空闲内存空间就放空的内存空间中;
2.如果没有就覆盖其他进程的数据,覆盖最不活跃的page(页面置换),而被覆盖掉的page保存在硬盘上。

解决安全问题
空间问题已经解决了,但是安全问题是怎么解决的呢?
dylib的加载过程中系统为了安全考虑引入了ASLRAddress Space Layout Randomization)技术和代码签名。
ASLR技术:镜像Image、可执行文件、dylibbundle在加载的时候,会在其指向的地址(preferred_address)前面添加一个 随机数偏差(slide),防止应用内部地址被定位。

四、rebase/binding

  • rebase

当程序被加载到内存的物理地址是随机的(可执行文件里的数据访问每次被加载都是物理地址随机的),但是在虚拟地址每次都是从0开始的。这样的安全是不足的。

所以苹果引入了ASLR技术(地址空间随机化),它是为了让程序每次启动时的虚拟地址不是从0开始,而是从一个随机的值开始。

在程序编译时基于ASLR计数产生的虚拟内存地址偏移的问题,在程序运行的时候通过rebase进行指针修复,从而保证访问可执行文件里的数据时的准确性。

mach-o中的每一条数据的记录都会有一个offset偏移值。

rebase的过程 = offset + ASLR值

  • binding

iOS编译过程-LLVM知道:在源代码被编译成可执行文件的过程中,对目标文件.o进行静态链接,静态链接器会对于一些外部的符号进行标记(该符号属于哪个库的),再生成可执行文件。

查看链接后的符号 $ xcrun nm -nm main // main为应用程序

以上面的截图为例:
iOS程序启动在调用这个符号_printf的时候,这个dyld_stub_binder就会在共享缓存中的libSystem动态库里面找到_printf真实的函数实现地址,来和_printf这个函数符号进行绑定。(注意:绑定的符号不仅只有dyld_stub_binder
符号和实现地址绑定就叫binding

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

推荐阅读更多精彩内容

  • 本文读者: 遭遇应用启动速度慢问题的朋友 希望保持应用启动速度快的朋友 对操作系统知识感兴趣的朋友 内容概览 理论...
    FicowShen阅读 293评论 0 0
  • 原文链接:http://www.zoomfeng.com/blog/launch-time.html 背景 一个项...
    CrystalZhu阅读 1,311评论 0 5
  • 背景 一个项目做的时间长了,启动流程往往容易杂乱,库也用的越来越多,APP的启动时间也会慢慢变长。本次将针对iOS...
    lp_lp阅读 1,560评论 0 12
  • 背景 一个项目做的时间长了,启动流程往往容易杂乱,库也用的越来越多,APP的启动时间也会慢慢变长。本次将针对iOS...
    高思阳阅读 434评论 0 1
  • 背景 一个项目做的时间长了,启动流程往往容易杂乱,库也用的越来越多,APP的启动时间也会慢慢变长。本次将针对iOS...
    酱油瓶2阅读 3,488评论 0 12