当我们的APP
跟着事务的添加、复杂,代码量也随之暴增,渐渐的翻开我们的App
时感觉非常卡,发起比较缓慢,非常影响用户的体会,那么怎么才能使我们的App
发起比较流通,给用户很好的体会,这篇文章将给我们带来App发起优化相关的知识。
1 App发起流程分析
App的发起我们一般分为两个部分:
-
main
函数之前即pre-main
-
main
函数之后
1.1 pre-main阶段流程
在 DYLD
环境变量DYLD_PRINT_STATISTICS
监测一下pre-main的时间消耗,我们在xcode中设置一个参数,如图:
我们发起App,看下输出成果,如下图
共耗时 300ms 左右:
-
dylib loading
加载动态库的时间 -
rebase/binding
重定向/绑定的时间 -
ObjC setup
OC类的注册时间 -
initializer
注册方法的时间(load,结构函数的耗时)
这就是pre-main
的基本流程
1.2 dylib
动态的加载
dylib的加载的耗时是必定的,系统的动态库是现已载入同享缓存空间,系统动态库现已做了高速的优化, 但是我们自定义的动态库不一样,所以苹果不主张自定义动态库
1.3 ObjC setup
由于我们的OC
是动态言语,OC
类的注册
- 读取
Mach-o
的data
字段,找到OC
类的相关信息 - 注册
OC
类,OC
的runtime
需求保护映射表即SEL/IMP
的映射以及类名与类的大局表,当加载Mach-o
的时分,这些一切的类都要注册到大局表中,除这些之外,还有类别
,协议
信息要刺进到方法列表中,这是必定的损耗,所以这儿的优化删去无用的OC
的类文件(只需这个类存在即使没有用到,也会构成时间损耗)
1.4 initializer
在load
方法以及结构函数中,尽量不要做延迟加载的作业,把消耗的使命放在子线程去,以减少主线程的开支,数据可以缓存。
以上几点的优化都比较简单,下理我们来介绍rebase/binding
重定向/绑定,再介绍之前,我们先来讲来虚拟内存相关的知识
2 物理/虚拟内存介绍
物理内存
-
在前期的操作系统,
CPU
直接从内存条读取数据,这就导致了内存不够用
问题,如下图:-
运用需求加载时就会直接加载到内存条中,然后
CPU
去内存条读取。当加载的运用多了,再加载新的运用时内存就不够用
了,这时分就需求干掉一些运用然后再去翻开这个新的运用。 -
加载到内存条中的运用都是根据内存地址去读,那么假设通过一些外挂加载到内存,然后可以在遍历内存条中地址访问到其他运用的内容,就导致账号被盗等安全问题,所以这个方法也是
不安全
的
-
-
怎样处理这些问题呢?工程师们发现加载到内存中的运用,大多数之用到一小部分功能,这就导致了资源的浪费,于是就发生了
懒加载
。懒加载是引用加载到内存中时,先加载发起相关的,后面要用到就再加载到内存条。
这样虽然会减少内存的占用,但是运用接下来的内存不知道要分配到哪,这样就导致代码不连续
,需求在运行进程不断核算地址很不方便
,而且功率很低
,于是工程师们就发明晰虚拟表
,也就引出了虚拟内存
虚拟内存
-
有了虚拟表,运用程序就只读代码,而代码核算地址的作业交给
CPU
和硬件MMU
,MMU
是内存办理单元
,它只做一件事:翻译地址
:- 这样运用程序的在运行时访问的内存就是连续的,而访问的内存就是
虚拟内存
,虚拟内存对应的就是核算好的物理地址。 - 虚拟地址和物理地址
不是
一个字节一个字节对印的,这样功率就很低。由于现在的运用内存是一块一块的,爽性就以块为单位去对印,单位就是page
,此时就发生了内存分页办理的概念
- 这样运用程序的在运行时访问的内存就是连续的,而访问的内存就是
映射表(内存分页)
-
页的大小在不同的操作系统中是不一样的。在
iOS
中(64位)
一页是16K
,在MAC
中是4K
。在MAC
中可以运用环境变量PAGESIZE
检查 -
现在
内存连续
的问题得以处理,安全问题
也处理了。由于一个运用只访问自己的虚拟表,而翻译出来的物理地址也是固定的,所以那些外挂就无法访问其它运用。内存溢出
的问题也处理了,由于现在内存都是一页一页的访问,所以就不会发生溢出。 -
当运用加载时,首要发起时需求的代码会直接加到内存,当要实行新的代码时,
cpu
发现这段代码内存中没有就会把代码卡住,然后操作系统会将这页加到物理内存中,这个现象就叫做缺页间断(pagefault)
。 -
操作系统需求实行的一页代码载入到物理内存中时,会往空缺处刺进。但手机发起后,物理内存就没有空位了,里边都放了其它的一些数据,但终究往哪里加呢,这个由操作系统决议。操作系统供给一个算法:
页面置换算法
,它会掩盖掉
不那么活跃的部分。所以有时分多翻开些运用后,再去进入第一次翻开的运用时会从头发起。 -
有时分在访问内存时会访问比
App
当前大的内存,由于虚拟表有8G
大小,能访问的有4G
,那么他会访问到其它运用吗?不会,这块虚拟内存是没有数据,它指向NULL
:
rebase/binding
-
binding
是绑定,当内部文件访问外部函数,就要通过内部符号绑定访问外部,现在的绑定方法都是懒加载
-
rebase
是重定位。由于虚拟内存发生后,每次内存都是从0
初步,这个时分相应的安全就不存在了,所以操作系统就出现了一个新技术ASLR
:让每次生成的虚拟页面,不要从0
初步,从随机的值初步,运用的每次发起的开端方位都是随机的。此时行数的方位就成了ASLR+OFFSET
,也就是rebase
重定向 -
少数缺页间断几乎是感知不到的,时间是毫秒级别。但一起有许多的缺页间断,比如成百上千乃至更多这个时分用户就能感知到了,冷发起时就会发生许多的缺页反常。
System Trace
检查缺页数
翻开 Instruments
选择 System Trace
:
选择设备和APP
:
发起完成后点击:
点击发起完成,自动解析,解析完成后 查找 main thread
– 选择 Virtual Memory
。 File Backed Page In
就是缺页信息,可以看到缺页间断的个数以及所用的时间:
通过调查发现消耗的总时间中,PageFault
占绝大部份
紧接在再次发起,缺页数和时间都变得很小了,由于此时运用的物理内存还没被掩盖,所以发起会时间会少许多
-
冷发起
:运用在物理内存中没有占用时的发起是冷发起。例如第一次翻开App
,或许APP
被杀身后,一段时间往后再翻开,都是冷发起 -
热发起
:运用编译在物理内存中的内存还存在的发起就是热发起,例如短时间APP
从后台回来,APP
杀身后立即翻开,此时它的物理内存还存在,此时就是热发起。
假设减少PageFault
的个数,就会到达优化的目的,具体操作下篇文章再进行解说