iOS优化-启动优化

2021-08-31  本文已影响0人  程序员进阶

测量启动过程的耗时

当按下home键的时候,App进程并不会马上被干掉,还会在后台存活一定时间。在这个时间内如果再次回到App那么几乎不需要做什么,就可以还原到退出前的状态。这种持续存活的情况下启动App,我们称为热启动,相对而言冷启动就是App被kill掉以后一切从头开始启动的过程。

测量main()函数之前的启动时间

苹果已经提供了这个测量方式

在Xcode的菜单中选择Project→Scheme→Edit Scheme,然后找到Run → Environment Variables,为项目添加环境变量DYLD_PRINT_STATISTICS值为YES

这样Xcode在启动App后会在控制台输出启动耗时

虚拟内存和物理内存

早期计算机没有虚拟内存这个问题,所有的地址都是实实在在的物理地址。当应用加载到内存中时,是全部加载到内中。内存的访问都是直接访问物理内存地址,这样极其不安全。然后就是很多时候一个应用真正只是用到少量的内存,这样就存在内存的浪费。因此出现了虚拟内存,让每个应用在逻辑上存在一大片连续的虚拟内存,每个进程的虚拟内存对应一个映射表,映射到实际的物理内存上。

那么cpu在访问这个进程的时候,先通过虚拟地址寻址,然后转换为对应的物理地址(地址翻译,这需要对应的硬件--cpu的内存管理单元mmu与操作系统配合)。

也就是说,进程中使用的地址是一片单独连续的虚拟地址,通过进程映射表(页表)映射到物理内存中,这时候进程在物理内存上占用的空间不一定是连续的。这样就解决了安全和物理内存使用率的问题。

内存分页

进程有自己的虚拟内存,但是物理内存实际也没有增大,这时候要解决内存的使用效率。

进程自己对应的映射表是以页为单位,在macOS上是以4KB一页为单位,iOS是以16KB为单位。(终端输入PAGESIZE可以查看到macOS的分页大小)。

如何解决内存浪费的?

应用程序加载到内存中时,并不会全部加载到物理内存中,属于懒加载,用哪一部分就加载那一部分。当访问进程的内存地址时,首先看页表,查看所要访问的对应页表是否已经加载到内存中。如果这一页没有在物理内存中时,操作系统会阻塞当前进程,发出一个缺页异常/缺页中断(pagefault),让后将磁盘中对应页的数据加载到内存中,完成虚拟内存和物理内存的映射。

当前进程的页表数据加载到物理内存中时,不一定是连续的,也有可能会覆盖其他进程的不活跃页,这样的按需分配,极大提高内存的使用效率。

虚拟内存的安全问题

虚拟内存通过页表映射到物理内存上,因此直接访问物理地址并不能实际正确的拿到进程的数据,但是进程的虚拟内存地址相对于自己来说也是绝对的,不管程序运行多少次,如果访问同一个函数,它在虚拟内存中的地址都是一样的这样也存在安全问题(比如直接静态注入)。

这样也出现了新的技术--ASLR(Address Space Layout Randomization)。

每次虚拟内存在加载之前,都加一个随机偏移值。iOS好像是从4.3版本开始了。

二进制重排(clang插桩实现二进制重排

缺页中断/缺页异常:内存分页管理,每一页加载的时候都会发生。

在iOS中,在加载缺页内存的时候,不仅发生缺页阻塞从磁盘中加载数据,还要对加载的这页做签名的验证

在使用中,我们一般感受不到这个过程,但是在启动中,这个过程也许你能很好的体会过。启动时,程序有大量的代码需要加载、执行,那么这个缺页中断有可能就很明显了。

如何优化?

假如我的app需要加载10页内存,但是启动的时候需要加载的代码放在1、3、5页。这时候来看看,代码在mac-o文件中的位置是根据文件加载生成的顺序来决定。那么这时候app启动需要运行的代码放在3个虚拟内存页中就需要出现3次pagefault。

如果我们那需要启动时用的代码全部放在前面1-2页中,甚至如果代码足够小只需要1页就够了,这样极大减少进程的阻塞。这也就是二进制重排。

查看pagefault

Xcode提供相关的调试工具,使用自带的instruments查看MainThread中虚拟内存的file backed page in项目,它代表着启动时,产生的pagefault次数。

二进制重排的优化是发生在编译链接阶段,对即将生成的二进制可执行文件进行重排。

xcode使用的连接器叫ld它可以指向一个order_file文件,在这个文件中指定排列符号,那么Xcode在编译时会按照指定的排列编译出可执行的文件,苹果自己也是这么用的。

虚拟内存工作原理

引用了虚拟内存后 , 在我们进程中认为自己有一大片连续的内存空间实际上是虚拟的 , 也就是说从0x000000 ~ 0xffffff我们是都可以访问的 . 但是实际上这个内存地址只是一个虚拟地址 , 而这个虚拟地址通过一张映射表映射后才可以获取到真实的物理地址 .

什么意思呢 ?

实际上我们可以理解为 , 系统对真实物理内存访问做了一层限制 , 只有被写到映射表中的地址才是被认可可以访问的 .

例如 , 虚拟地址0x000000 ~ 0xffffff这个范围内的任意地址我们都可以访问 , 但是这个虚拟地址对应的实际物理地址是计算机来随机分配到内存页上的 .

这里提到了实际物理内存分页的概念 , 下面会详细讲述 .

(可能大家也有注意到 , 我们在一个工程中获取的地址 , 同时在另一个工程中去访问 , 并不能访问到数据 , 其原理就是虚拟内存 .)

整个虚拟内存的工作原理这里用一张图来展示 :

1、查找没有使用的文件,需要删除(用脚本工具)

2、

参考:https://www.jianshu.com/p/9ba858461e6d

上一篇下一篇

猜你喜欢

热点阅读