启动之前检测耗时
Main函数之前 pre-main
Main函数之后
其实iOS已经给我们提供了监测Dyld的选项,我们只需要配置下就可以
运行,控制台输出
- dylib loading time: 46.99 milliseconds (206.9%): 动态库载入 苹果建议6个
- rebase/binding time: 126687488.9 seconds (68758989.5%): 重定位 绑定
- ObjC setup time: 8.95 milliseconds (39.4%): OC类注册
- initializer time: 32.13 milliseconds (141.5%): load 构造函数的耗时
前言虚拟地址
早期使用物理内存阶段:
1.安全问题:虚拟内存也解决了安全问题,不能直接操作内存了。
2.内存不够用 => 整个加载到内存条上 => 后面发展:用到才加载懒加载 => 造成内存地址不连续 => 出现了一个映射表(CPU MMU翻译地址)虚拟地址和物理地址一一对应,虚拟地址是连续的 => 内存分页管理page
在iOS一页=16k
在Mac一页=4k
有了虚拟内存之后,做到了进程和进程之间的安全隔离, 每一个进程都是自己的一块虚拟内存。
虚拟内存页的大小:4G
缺页异常:在进程1中操作了P4,此时P4只是虚拟内存还没有加载到物理内存,此时会报pageFault缺页异常、缺页中断,CPU中断,操作系统就开始吧P4对应的数据加载到物理内存中。
页面置换:基本上物流内存是没有多余的空位,此时用到了页面置换,系统的置换算法:覆盖掉不那么活跃的部分。
系统为了进程之间的通信:给了专门的接口,保证进程之间的安全(共享缓存)
iPhone6s开始,物理内存的Page大小是16k,6和之前的设备都是4K
32位和64位其实是硬件的区别:CPU数据总线
rebase重定位
由于存在了虚拟内存,那么我们应用程序在物理内存上的加载都是随机的。所以文件中的数据访问都是随机的。每次应用程序启动,内存都是从0开始,所以此时相对应的安全又不存在了。后面出现了ASLR让每次生成的地址不是从0开始,从一个随机的位置开始。内部的文件都会根据ASLR的起始位置偏移一定的量
ASLR + offset
:这个动作就叫做rebase重定位
二进制重排
当我们的代码访问到没有被加载到内存的代码时,此时会有缺页中断。虽然是毫秒级别的,但是如果有大量的缺页异常的时候,耗时就存在一定的优化空间,在App冷启动的时候存在大量的缺页异常,这个是必然的。 把启动时刻的方法手动的排到前面,这就叫做二进制重排
- 首先在配置里面把
Write Link Map File
设置为YES
- Build 成功之后,show in finder, 找到二进制文件
- 打开Libobjc的源码,找到这个
libobjc.order
文件,这个文件就是决定了编译器执行二进制文件的顺序 - 自定义order文件,在该文件的根目录下新增一个order文件,里面都是一些符号
- 使用
- Bulid 再次打开二进制文件
我们发现这个执行顺序就是按照自定义order的顺序,如果没有的符号会过滤掉。回到主题,所以我们应该知道在启动时刻调用了哪些方法,才能进行重排。
写在最后-思考
-
我们可以hook所有的方法,但是无法追踪C函数和block
-
我们下一篇介绍100%hook到所有的方法和函数-Clang插桩。