一 APP启动过程
a) 冷启动的定义:从用户点击App图标开始 到用户能看到首页为止
b) 冷启动过程分为 T1 T2 T3三个时间
T1 T2过程如下
T1:main()函数之前,即操作系统加载App可执行文件到内存,然后执行一系列的加载&链接等工作,最后执行至App的main()函数。
T2:main()函数之后,即从main()开始,到appDelegate的didFinishLaunchingWithOptions方法执行完毕。
T3 是指:didFinishLaunchingWithOptions 结束 -> 用户看到首页
c) T1 T2 T3 这三个流程,分别都做了什么
- T1:main()函数之前,即操作系统加载App可执行文件到内存,然后执行一系列的加载&链接等工作,最后执行至App的main()函数. 后面称为pre-main阶段
具体做了哪些:
- 系统内核(Kernel)创建一个进程
- 加载可执行文件。(可执行文件是指Mach-O格式的文件,也就是App中所有.o文件的集合体)
- 加载dyld,最后 dyld 会调用 main() 函数,main() 会调用 UIApplicationMain(),before main()的过程也就此完成
dyld做了哪些:
- 加载动态库:Dyld从主执行文件的header获取到需要加载的所依赖动态库列表,然后它需要找到每个 dylib,而应用所依赖的 dylib 文件可能会再依赖其他 dylib,所以所需要加载的是动态库列表一个递归依赖的集合
- Rebase和Bind:Rebase在Image内部调整指针的指向。在过去,会把动态库加载到指定地址,所有指针和数据对于代码都是对的,而现在地址空间布局是随机化,所以需要在原来的地址根据随机的偏移量做一下修正,Bind是把指针正确地指向Image外部的内容。这些指向外部的指针被符号(symbol)名称绑定,dyld需要去符号表里查找,找到symbol对应的实现
- Objc setup:注册Objc类 (class registration)、把category的定义插入方法列表 (category registration)、保证每一个selector唯一 (selector uniquing)
- initializers:Objc的+load()函数、C++的构造函数属性函数、非基本类型的C++静态全局变量的创建(通常是类或结构体)
T1 流程图如下
- T2 :didFinishLaunchingWithOptions方法
主要是大量的启动任务:比如支付SDK初始化,地图SDK初始化,日志SDK初始化等等。还有一些隐晦的耗时操作,IO操作
- T3:主界面的构建
d)总结:这是一个网上的完整启动流程图,可以参考
二 针对启动流程的各个阶段 业界都是做了哪些优化
1) T1 阶段优化
1.1 影响T1时间的因素:
- 动态库加载越多,启动越慢
- ObjC类,方法越多,启动越慢
- ObjC的+load越多,启动越慢
- C的constructor函数越多,启动越慢
- C++静态对象越多,启动越慢
1.2 业界都做了哪些优化
- 代码瘦身:删除无用的类,无用的方法和资源文件、动态库,随着业务的迭代,不断有废弃的代码合资源文件。参考这个网址删除 objc_cover
- +load优化:有些类会在+load方法中做一些操作,过多+load方法则会拖慢启动速度。可以将
+load中的内容,放到渲染完成后做,或者使用+initialize()的方法代替+load(),注意避免+initialize()的重复调用问题 - 合并多个动态库:苹果公司建议最多使用 6 个非系统动态库。
- 二进制重排:针对mach-o 文件的优化,目的在于将hot code聚合在一起,即使得最经常执行的代码或最需要关键执行的代码(如启动阶段的顺序调用)聚合在一起,形成一个更紧凑的__TEXT段。具体优化参考这些文章 抖音启动优化 二进制重排
2)T2阶段优化
- 避免大量IO操作,比如 存储图片和文件等资源,图片解码、archive文档等
- 很多操作是串行执行的,若干个任务串行执行,时间必然比较长。如果能变串行为并行,那么冷启动时间就能够大大缩短
- 启动项的治理:分阶段启动对应的业务,有些启动项是需要刚启动就执行的操作,如Crash监控、统计上报等;有些启动项需要在较早的时间节点完成,例如一些提供用户信息的SDK、定位功能的初始化、网络初始化等;有些启动项则可以被延迟执行,如一些自定义配置,一些业务服务的调用、支付SDK、地图SDK等
3) T3 阶段优化
主要是使用缓存进行优化。比如定位缓存,数据缓存
四 生活APP治理方案
a)现有阶段 可以做哪些优化 哪些收益较大 以及方案
1 T1 阶段的优化
1.1 梳理 并删除 无用的系统动态库,无用的第三方SDK
1.2 删除无用的类
1.3 梳理各个类的+load方法、改善 hook load方法的行为
1.4 删除无用的方法
1.5 删除无用的资源文件,比如图片
1.6 二进制重排 mach-o文件改造
总结:根据业界优化经验,T1阶段的优化产生的实际效果并不是很大,投入产出比较低,可以后续优化,所以在方案中的优先级较低。 现在 可以将1.1 、1.2 、1.3 、1.5 做出来,1.4、1.6未来再探索
2 T2 阶段优化
梳理 和分类 各个启动项,将可以延迟启动的启动项 梳理出来(ps:待优化完成后,会将各个启动项,分类别的更新到这个文档)。分类原则如下:
- 不能延迟的初始化项---信息收集类:Crash监控,日志统计上报 ,性能监控 不能延迟初始化,且需要在 较早节点 完成初始化,比如bugly 神策
- 因为各种原因,需要在didFinishLaunchingWithOptions进行初始化的项目。做的过程中需要思考,是否可以用别的方式 延迟初始化
- 可以延迟初始化的项目:自定义配置,比如键盘颜色,支付SDK等
总结:1.这个阶段优化的效果比较理想,投入产出比较大,所以在方案中的优先级较高。2.重点是梳理出来可以延迟初始化的启动项,并延迟初始化
(待补充:点击链接跳转进来)
3.T3 阶段优化
3.1 SplashViewController YHTabbar 耗时较多,优化里面的逻辑
3.2 首页的业务逻辑优化,尽可能早的渲染页面
总结:3.1 优化空间较大,所以在方案中的优先级较高。3.2因为首页是一个逻辑特别重的页面,并且跟业务强耦合,投入产出比较低,未来可以再做。
b)生活APP 未来可以做哪些优化
1.通过分析mach-o 删除不用的方法
2.组件化完成后 可以探索mach-o文件的优化,不限于二进制重排
3.首页业务逻辑的优化
五 启动的持续监控
现在日志已经上传启动时间,后续可以做一个告警,超过预警线就告警
六 生活的第一阶段启动优化成果
TODO 待优化完成后补充
引用资料