对于一个可执行文件来讲,它的加载过程是: 分为两大部分:缓存
操做系统加载可执行文件,经过fork(建立一个进程)指令在新的空间内来执行可执行文件,加载依赖的可执行文件(mach-o)文件,定位其内部与外部指针引用,例如字符串与函数,执行声明为attribute((constructor))的C函数,加载扩展(Category)中的方法,C++静态对象加载,调用ObjC的+load函数bash
基本流程:多线程
App 开始启动后,系统首先加载可执行文件(自身 App 的全部 .o 文件的集合),而后加载动态连接器 dyld,dyld 是一个专门用来加载动态连接库的库。 执行从 dyld 开始,dyld 从可执行文件的依赖开始,递归加载全部的依赖动态连接库。 动态连接库包括:iOS 中用到的全部系统 framework,加载 OC runtime 方法的 libobjc,系统级别的 libSystem,例如 libdispatch(GCD) 和 libsystem_blocks (Block)。app
动态连接库的加载过程主要由dyld来完成,dyld是苹果的动态连接器。框架
官方文档: developer.apple.com/library/arc…异步
Mach-O是OS X中二进制文件的本机可执行格式,是传送代码的首选格式。可执行格式肯定二进制文件中的代码和数据被读入内存的顺序。代码和数据的排序会影响内存使用和分页活动,从而直接影响程序的性能。段的大小经过其包含的全部段中的字节数来度量,并向上舍入到下一个虚拟内存页边界。 Mach-O二进制文件被组织成segements。每一个segement包含一个或多个部分。每一个部分都有不一样类型的代码或数据。segement始终从页面边界开始,但section不必定是页面对齐的。所以,segement终是4096字节或4千字节的倍数,其中4096字节是最小大小。 Mach-O可执行文件的segement和section根据其预期用途命名。segement名称的约定是使用以双下划线开头的全大写字母(例如,TEXT); section名称的约定是使用以双下划线开头的全小写字母(例如, text)。 Mach-O可执行文件中有几个可能的segements,但只有两个与性能有关:__TEXT段和__DATA段。函数
The __TEXT Segment: Read Only __TEXT segment是包含可执行代码和常量数据的只读区域。按照惯例,编译器工具建立具备至少一个只读__TEXT segment的每一个可执行文件。因为该段是只读的,所以内核能够将__TEXT segment直接从可执行文件映射到内存中一次。当segment被映射到内存时,它能够在全部进程之间共享其内容。 (这主要是框架和其余共享库的状况。)只读属性还意味着构成__TEXT segment的页面永远没必要保存到后备存储。若是内核须要释放物理内存,它能够丢弃一个或多个__TEXT页面,并在须要时从磁盘从新读取它们。 __TEXT segment的主要部分,sections分布工具
The __DATA Segment: Read/Write __DATA segment 包含可执行文件的很是量变量。该 segement 是可读写的,由于它是可写的,因此对于与库连接的每一个进程,逻辑上复制静态库或其余动态共享库的__DATA段。当内存页面可读写时,内核会使其变为copy-on-write。此技术能够作到,动态库是在内存中共享的,能够被其余各个进程访问,但由于__DATA Segment是可读可写的,就会经过某一进程对共享的_DATA Segment有写操做的时候,再进行单独的_DATA内存空间复制。 __DATA segment 有许多部分,其中一些仅由动态连接器使用。下面 列出了能够出如今__DATA segment 中的一些更重要的部分。有关段的完整列表,请参阅Mach-O运行时体系结构。布局
Mach-O 性能影响 Mach-O可执行文件的__TEXT segment和__DATA segment的组成与性能有直接关系。优化这些sections的技术和目的是不一样的。可是,它们的共同目标是:提升内存使用效率。性能
最典型的Mach-O的文件由可执行代码组成,在__TEXT,__text当中。如__TEXT segment,该__TEXT是只读的,并直接映射到可执行文件,因此若是内核须要回收某些__text页面占用的物理内存,就没必要将页面保存到back store再将其分页。它只须要释放内存,并在后面代码引用的时候从磁盘从新读回。虽然这比交换内存分页的成本低,由于这只是一个磁盘访问,而不是两个内存分页的交换 , 但这仍然很损耗性能,特别是若是必须从磁盘从新建立许多页面。
对于这种状况的改进,是经过程序从新排序来改进代码的引用位置,如改进参考位置中所述。该技术将方法和功能组合在一块儿,具体取决于它们的执行顺序,调用频率以及它们相互调用的频率。若是__text部分组中的页面以这种方式逻辑上起做用,则它们不太可能被屡次释放和读回。例如,若是将全部启动时初始化函数放在一个或两个页面上,则在发生初始化后没必要从新建立页面。
与__TEXT段不一样,__DATA能够写入段,所以段中的页面__DATA不可共享。框架中的很是量全局变量可能会对性能产生影响,由于与框架连接的每一个进程都会得到这些变量的副本。解决这个问题的主要解决办法是尽量多的非恒定的全局变量尽量转移到__TEXT,__const经过宣布他们部分const。减小共享内存页面描述了此技术和相关技术。这一般不是应用程序的问题,由于应用程序中的__DATA部分不与其余应用程序共享。
编译器将不一样类型的很是量全局数据存储在段的不一样部分中__DATA。这些类型的数据是未初始化的静态数据和符号与未声明的“暂定定义”的ANSI C概念一致extern。未初始化的静态数据位于__bss段的__DATA部分中。暂定的符号在__common 该__DATA部分。
该 ANSI C和 C ++标准指定系统必须将未初始化的静态变量设置为零。(未初始化的其余类型的未初始化数据。)因为未初始化的静态变量和临时定义符号存储在单独的部分中,所以系统须要以不一样方式对待它们。可是当变量位于不一样的部分时,它们更有可能最终出如今不一样的内存页面上,所以能够单独进行交换,从而使代码运行速度变慢。如减小共享内存页面中所述,这些问题的解决方案是在段的一个部分中合并不是常量全局数据__DATA。
dyld的加载过程会初始化Runtime系统,在此阶段,有至关多的优化工做能够作
在Runtime系统加载之后,开始进行初始化
从上面能够得出如下几个结论,影响该阶段启动时间的因素以下:
static int x;
static short conv_table [128];
//更换为
static int x = 0;
static short conv_table [128] = {0};
复制代码
减小静态变量的使用 3. 减小符号表的导出 经过设置-exported_symbols_list或-unexported_symbols_lis来限制符号表的导出,从而减小dyld的工做量 4. 去除没有使用的动态库依赖,明确所依赖的frameworks是require仍是optional,optional会动态进行额外检查 5. 删除没有用的方法 6. 减小+load函数的实现,并减小在其中操做的逻辑 7. 对某些常常调用的代码进行二进制化,生成静态库,多使用静态库代替动态库,将多个静态库框架,集中制做成静态framework,从而可以减小dyld的连接工做 关于冷启动和热启动的不一样以下: