iOS 开发每天分享优质文章自鉴

APP启动过程详解+优化(二进制重排)

2021-02-04  本文已影响0人  冼同学

APP的启动过程

冷启动:APP在完全杀死的状态,需要系统新创建一个进程分配给它的情况。这是一次完整的启动过程。(二进制重排主要针对冷启动的优化)

热启动:App 在冷启动后,用户将App 退到后台,即在App的进程还在系统里的情况下,用户重新启动进入 App 的过程,这个过程做的事情非常少,启动速度非常快。

启动时间的组成

启动时间得可以根据main()函数前后进行划分,大致分为以下时间段。

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

2.t2阶段,main()及main()之后处理所需时间。

3.首屏渲染完成后。

测量 pre-main 时间

Edit scheme -> Run -> Auguments 添加环境变量 DYLD_PRINT_STATISTICS,value设为YES(图1)。

图1

然后启动项目,就可以看到各方面的时长(图2)。

图2

由图2可以知道  t1阶段:pre-main流程如下(图3):

图3

加载dylib:

加载可执行文件(App 的.o 文件的集合),加载动态链接库。

在dylib的加载过程中系统为了安全考虑引入了ASLR(Address Space Layout Randomization)技术和代码签名。

ASLR技术:镜像Image、可执行文件、dylib、bundle在加载的时候会在其指向的地址(preferred_address)前面添加一个随机数偏差(slide),防止应用内部地址被定位。

rebase/bind

dylib加载完成之后,它们处于相互独立的状态,需要绑定起来。

rebase:将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO。(结合ASLR偏移量,计算好地址)。

bind:查询符号表,进行方法绑定,修正镜像内部的指针。

Objc Set Up

Objc 运行时的初始化处理,包括 Objc 相关类的注册、category 注册、selector 唯一性检查等;

initializer

initializer:初始化,包括了执行 +load() 方法、attribute((constructor)) 修饰的函数的调用、创建 C++ 静态全局变量。

总结:结果上面的分析,得到得优化方案有:

1.减少动态库加载,每个动态库都有其的依赖关系。

2.减少加载启动后不会去使用的类或者方法。

3.+load方法的内容尽量在加载完成后执行,或使用 +initialize()方法替换掉,尽量进行懒加载。

注意:在进行二进制重排之前我们还需要知道内存是怎么操作的。

内存管理

内存是分页管理的,映射表(虚拟内存)不是以字节为单位,是以页为单位的。macOS,Linux一页的大小为4k,iOS是一页大小为16k。

物理内存

早期的计算机没有虚拟内存这一说,系统的程序运行都会全部加载到内存中,这样子就会造成了大量资源的浪费。如果内存不够,程序将无法打开。必须先关闭前面的部分应用才能继续开启,这样子性能就会存在很大的问题。其次,因为程序放到了物理内存且内存地址是连续性的,别人通过其他运行中的软件访问到物理内存的话,就会跨程序访问到其他程序的物理内存,找到对应的点进行攻击,这里也是一个非常大的安全问题。如图4:

图4

虚拟内存

针对物理内存的种种问题与弊端,工程师们就提出了虚拟内存,完美的解决了以上的问题。首先我们的程序加载时候会进行模块化(分页)处理,生成了一张 虚拟内存和物理内存关联的表 --映射表(地址为0 - 4G)。图5:

图5

当App需要使用某一块虚拟内存的地址时,会通过这张表查询该虚拟地址是否已经在物理内存中申请了空间。如果已经申请了则通过表的记录访问物理内存地址,如果没有申请则申请一块物理内存空间并记录在表中(Page)。这个通过进程映射表映射到不同的物理内存空间的操作叫地址翻译,这个过程需要CPU和操作系统配合。(注意:分页的首地址不是从映射表的0开始的,虚拟内存地址分配还会涉及到 ASLR)

分页异常(Page Fault)

在分页加载的过程中,当分内没有存在内存的时候,系统就会阻塞该进程,将磁盘中对应Page的数据加载到内存,把虚拟内存指向物理内存。在启动的过程中会存在大量得分页异常,就会重复大量的做阻塞和重载操作,那么为了解决这问题,就引入了二进制重排。

补充:如果我们的内存加载满了,内存就会根据程序的活跃度来释放分页。平时我们放久的微信重新进去就会被移除内存,会出现冷启动的现象。

二进制重排

重排目的:二进制重排就是为了减少启动时因为分页异常而浪费的时间。

没重排 已重排

由上可知,二进制重排主要是启动时必须要加载的page放到前面优先加载,这样子就会减少分页缺失的发生,从而达到优化的效果。

查看 Page Fault

Instruments -> System Trace

File Backed Page In 即为 Page Fault 的个数。

链接映射文件 Link Map File

Link Map File的配置和获取

sections部分信息如下:

由section信息可里可以看到启动时方法符号的排序。

order_file的引入

Xcode提供了排列符号的设置给开发者,设置 order_file 即可。objc 源码就采用了二进制重排优化。

order_file的配置

在根目录生成link.order文件,这里面就是方法符号的排序。

Target -> Build Setting -> Linking -> Order File 设置 order file 的路径

order file编辑

order file的生成参考。

注意:全手写一定是不可取的,想实现自动化就要解决下列问题:保证不遗漏方法,保证方法符号正确和保证方法符号顺序正确。

解决方案可见 《抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15%》

如果想了解编译期插桩的话请参考:https://juejin.cn/post/6844904170139418631。

上一篇 下一篇

猜你喜欢

热点阅读