前言

APP启动优化大致分为Main函数之前Main函数之后,Main函数之后的优化大多是使用懒加载的方式或者优化业务加载顺序,下面重点看一下Main函数之前的优化。所谓冷启动优化就是应用第一次加载进内存的优化。

Pre-main时间

Main函数之前都干了什么?Xc缓存视频在手机哪里找ode添加环境变量DYAPPLD_PRINT_STATISTICS,日志看一下p多线程编程re-main启动时间,此处demo以本人实际项目为例。

APP 冷启动优化
Xcode运行项目,日志输出如下:

**Total pre-main time: 774.24 milliseconds (100.0%)**
**dylib loading time: 138.83 milliseconds (17.9%)**
**rebase/binding time:  5.88 milliseconds (0.7%)**
**ObjC setup time: 83.71 milliseconds (10.8%)**
**initializer time: 545.80 milliseconds (70.4%)**
**slowest intializers :**
**libSystem.B.dylib :  9.07 milliseconds (1.1%)**
**libMainThreadChecker.dylib : 50.17 milliseconds (6.4%)**
**libglInterpose.dylib : 185.33 milliseconds (23.9%)**
**推送助手 : 525.02 milliseconds (67.8%)**

可以看到pre-main也就是main函数之前总耗时774.24毫秒

  • dylib loading动态库的载入。尽量减少自定义动态库,苹果12建议大于6个的话尽可能合并。
  • rebase/binding:重定位/绑定。macho中的缓存视频在手机哪里找内存地址是偏移地址,需要加上首地址ASLR变成真实的虚拟内存地址,这就是重定位。区是于链接,链接是apple编译时,绑定是运行时,绑定是绑定的共享缓存的地址,外部符号越多绑定时间相对越多线程渲染长。
  • ObjC setupOC类的注册。要尽可能的删除没有用到的类,虽然没有用到,但是只要存在,就会对类进行处理。
  • initializer timeload()以及构造函数。load()别做耗时的事情,这是启动时间优化的重点,一般是优化业务逻辑缓存视频变成本地视频
  • slowest intializers详细列出了ios下载initializer time中具体耗时情况,我的项目中耗时最多的是主工程的加载,耗时525.02毫秒

对于Main函数之前的优化,先抛开主工程的加载,优化建议如下:

  1. 减少自定义动态库,控制在6个以内
  2. ios14.4.1更新了什么没有用到的类
  3. load()别做耗时的事情。appointment

虚拟内存

优化主工程的加载之前,先了解一下什么是虚拟内存,简单总结如下。

  • 一开始程序加载的内存是物理内存,但是软件发展速度太快了导致物理内存不够用,毕竟一缓存的视频怎么保存到本地个程序就占用一块物理缓存文件夹名称内存,而且直接使用物理内存存在安全隐患,黑客很容易攻击这块内存。
  • 为了让内存安全以及内存够用就产生了虚拟内存,现在我们所说的内存都是虚拟内存,CPU上的一个模块会把虚拟内存翻译成物理内存,这样虚拟内存和物理内存就有一个appointment射表,为了高效的地址翻译,不可能一个字节一个字节的翻译,于是出现了内存分页管理,即以页为单位翻译,iphone6s之后一页是16字节,iphon6多线程和多进程的区别s之前一页是4字节,所以iphone6s之后启动时间会比较缓存文件夹名称快,因缓存的视频怎么保存到本地苹果官网内存翻译很快。虚拟内存地址很大有4G,这样就ios15解决了内存不够用的情况。
  • 程序加载苹果7会把虚拟内存根据映射表都加载进物理内存中,而是用到哪一页或哪几页就把用到的加载进去缓存是什么意思。比如当程序访问虚拟内存p1页时,如果物理内存中没有,就缓存视频变成本地视频会发生缺页中断,这时就要把这块虚拟内存加载进物缓存的视频怎么保存到本地理内存,ios程序的冷启动是发生缺页中断最多的地方,因为这时的物理内存中没有对应的虚拟内存,虽然加载进物理内存很快,但是如果有非常多缓存视频怎样转入本地视频的缺页中断,那么还是会影响效率的。

总结:

过多的缺页中苹果8就会影响A多线程渲染PP启动速度,所以我们要尽可能的减少缺页中断。比如启动Aapp小胖子PP时必须调用10个方法,但是这10个方法分布在10个不apple苹果官网同的页中,那application么就会发生10次缺页中断,如果把启动时必须调用的这10个方法放在同一个或者一缓存视频合并app下载两个页中,那么就会减少缺页中断的次数。下面的重点就是如苹果13重排二进制苹果xr减少缺页中断的次数。

二进制重排前

Xcodecommand+control+i多线程和多进程的区别inst缓存视频怎样转入相册ruments选中System Trace,点多线程的实现方式击启动,当APP启动后进入第一个页面,点击暂停,这时就会记录从启动到第一个页面的过程数据。搜索Main thread查看虚拟内存中缺页中断情况,如下图。

APP 冷启动优化
分析:缺页中断有384次,耗时58.69毫秒,启动耗时62.99毫秒,缺页中断耗时apple苹果官网占了大部分。优化缺页中断次数的前提是有没有浪费,有浪费的缺页中断才需appreciate要优化。

启动时函数的加载顺序

我们上面说为了减少缺页中断,需要尽可能的把启动时函数的调用放在同缓存的视频怎么保存到本地一个页中,下面看下整个应用里函数加载进内存中的顺苹果x序。

Xcode->build setting->LAPPink Map->Write Lin多线程编程k Map File->YES,编译后在bulid中查看这个link map文件如下:

APP 冷启动优化
link map中这些函数调用是根据编译顺序排列的,我们想要减少缺页中断,就要尽可能ios14.4.1更新了什么的把启动时的函数调用放在最前面。可能你会觉得在Xcode中重新排列文件的编译顺序就可以,但是我们是要把启动时调用的函数放在application最前面,而不是启动时的文件放在最前面,毕竟文件中的函数有很多,但是只有个别函数在启动时会调用。

如何重排二进制

那么我多线程是并发还是并行们如何找出APP启动时调用了哪些函数呢?Clang的插桩法可以很好的解决这个问题。根据Clang官方文档文档步骤如下:

1. Xcode->build setting->Other C Flags-多线程应用场景例子>-fsanitiz苹果xre-coverage=fuios越狱nc,trace-pc-guard

  1. 编写程序生成.order文件,比如gy.order,clang会根据此文件对link map文件中的函数重新排列,程序中需要实现的关键函数如下:
  • __sanitizer_cov_trace_pc_guard_init:app中有多少个函数就会有多少个符号
  • __sanitizer_cov_trace_pc_guard:hook所有回调函数,APP启动时调用了多少个函数,就会调用多iOS少次该函数。clang在app小胖子编译时会把该函数插入到函数的开始位置。

具体demo如下:

#import <dlfcn.h>
#import <libkern/OSAtomic.h>
//定义原子队列
static OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体
typedef struct {
  void * pc;
  void * next;
} SYNode;
//里面反应了项目中符号的个数!!
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                          uint32_t *stop) {
 static uint64_t N;
 if (start == stop || *start) return;
// printf("INIT: %p %p\n", start, stop);
 for (uint32_t *x = start; x < stop; x++)
  *x = ++N;
}
//HOOK一切的回调函数!!
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  void *PC = __builtin_return_address(0);
  //创建结构体
  SYNode * node = malloc(sizeof(SYNode));
  *node = (SYNode){PC,NULL};
  //结构体入栈
  OSAtomicEnqueue(&symbolList, node, offsetof(SYNode, next));
}
//生成order文件!!
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  //定义数组
  NSMutableArray<NSString *> * symbleNames = [NSMutableArray array];
  while (YES) {
    SYNode * node = OSAtomicDequeue(&symbolList, offsetof(SYNode,next));
    if (node == NULL) {
      break;
    }
    Dl_info info;
    dladdr(node->pc, &info);
    NSString * name = @(info.dli_sname);//转字符串
    //给函数名称添加 _,c函数或者block函数需要加上_,oc函数不需要
    BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
    NSString * symbolName = isObjc ? name : [@"_" stringByAppendingString:name];
    [symbleNames addObject:symbolName];
  }
  //反向遍历数组,最后一个元素是启动时最先调用的
  NSEnumerator * em = [symbleNames reverseObjectEnumerator];
  NSMutableArray * funcs = [NSMutableArray arrayWithCapacity:symbleNames.count];
  NSString * name;
    //去除重复调用的函数
  while (name = [em nextObject]) {
    if (![funcs containsObject:name]) {//数组没有name
      [funcs addObject:name];
    }
  }
  //去掉touchbegin自己,touchbegin是测试生成排序文件的函数
  [funcs removeObject:[NSString stringWithFormat:@"%s",__func__]];
  //写入文件
  //1.编程字符串
  NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
  NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"gy.order"];
  NSData * file = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
  [[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
  NSLog(@"%@",funcStr);
}

demo分析:

  • __sanitizer_coapplev_trace_pc_guard,hook函数是多线程的,所以为了线程安全,需要定义一个原子队列存放。
  • 结构体入栈函数OSAtomicEnqueueoffsetof(SYNode, next)目的是指定存放下一个函数的位置。
  1. 运行项目点击屏幕,拷贝沙盒中生成的gy.order文件放在项目根目录下,xcode->order File->./gy.ios系统order,同时需要删除Xcode->bu苹果8plusild setting->Other C Flags->-fsanitize-coverage=func,trace-p苹果官网c-guard配置。clang会根据gy.order重新排序函数编译的顺序,再来看一下link map是否重排成功了。

APP 冷启动优化

很明显link map确实改变了,我们再Syappearstem Trace看一下主线中缺页中断是否优化了。

APP 冷启动优化
对比一下没排序前,缺页中断有384次,耗时58.69毫秒,启动耗时62.99毫秒。现在缺页中断271,耗approach时40.03,启动耗时44.58,启动大概优化了不到20毫appear秒。目前测缓存视频在手机哪里找试的项目不是很大,如果项目很大的话,比如微信,那么这个启动优化还是很可观的。