iOS App启动优化(四):编译期插桩 && 获取方法符号
iOS App启动优化(五):收集符号 && 生成 Order File
内存管理
内存是分页管理的,映射表不能以字节为单位,是 以页为单位。
Linux
以4K
为一页macOS
以4K
为一页iOS
以16K
一页
终端输入
pageSize

4 * 1024 = 4096
内存浪费
早期的计算机不断启动应用,到达一定数量以后会报错,应用无法正常运行,必须先关闭前面的部分应用才能继续开启。
这是因为早期计算机没有虚拟地址,一旦加载都会 全部加载到内存中 。一旦物理内存不够了,那么应用就无法继续开启。
应用在内存中的排序都是顺序排列的,这样进程只需要把自己的地址尾部往后偏移一点就能访问到别的进程中的内存地址,相当不安全。

虚拟内存
用户使用时并不会使用到全部内存,如果 App
一启动就全部加载到内存中会浪费很多内存空间。 虚拟内存技术 的出现就是为了解决这个内存浪费问题。
App
启动后会认为自己已经获取到整个 App
运行所需的内存空间,但实际上并没有在物理内存上为他申请那么大的空间,只是生成了一张 虚拟内存和物理内存关联的表 。

地址翻译
当 App
需要使用某一块虚拟内存的地址时,会通过这张表查询该虚拟地址是否已经在物理内存中申请了空间。
- 如果已经申请了则通过表的记录访问物理内存地址,
- 如果没有申请则申请一块物理内存空间并记录在表中(
Page Fault
)。
这个通过进程映射表映射到不同的物理内存空间的操作叫 地址翻译 ,这个过程需要 CPU
和操作系统配合。
Page Fault
当数据未在物理内存会进行下列操作
- 系统阻塞该进程
- 将磁盘中对应
Page
的数据加载到内存 - 把虚拟内存指向物理内存
上述行为就就是Page Fault
灵活内存管理
虽然解决了浪费问题,但是万一物理内存空间全都被申请了呢?还是有可能产生内存不足的情况的,为保证当前App
的正常使用,数据加载遵循以下原则:
- 如果有空闲内存空间就放空的内存空间中
- 如果没有就覆盖其他进程的数据
- 具体覆盖由操作系统处理
解决安全问题
空间问题已经解决了,但是安全问题是怎么解决的呢?
在dylib的加载过程中系统为了安全考虑引入了ASLR
(Address Space Layout Randomization
)技术和代码签名。
ASLR技术:镜像Image
、可执行文件、dylib
、bundle
在加载的时候会在其指向的地址(preferred_address
)前面添加一个随机数偏差(slide
),防止应用内部地址被定位。
为什么进行二进制重排
虚拟内存技术会产生缺页中断(Page Fault
),这个过程是个耗时操作。
每页耗时也有很大差距,1微秒到0.8毫秒不等。
使用过程中对这点耗时感觉不明显,但是启动时加载大量数据,如果产生大量缺页中断(Page Fault
),时间叠加后用户会有明显感知。
如果我们把所有启动时候的代码都放在一页或者两页,这样就很大程度减少了启动时的缺页中断(Page Fault
)从而优化启动速度,这就是二进制重排。
二进制重排详情请移步 iOS App启动优化(三):二进制重排