几种常见错误会造成内存泄漏
1 使用c语言方法,需要手动释放
2 对文件进行操作,要手动关闭
3 调用block时,给block传指nil
4 数组中插入了nil元素
....
日常项目中常用的内存泄漏检测:
1 静态检测,使用Xcode,Product->Analyze静态分析
2 动态检测,使用Intruments->Leaks动态分析
3 析构检测,通过Runtime(AOP)监听系统dealloc方法
控制器正常的生命周期中会在viewDidDisappear:之后执行dealloc方法,从而进行内存的回收和释放。但是如果控制器中存在循环引用之类的问题,会导致控制器的dealloc方法无法执行,造成内存泄漏。
析构检测的原理就是监听控制器的生命周期,在viewDidDisappear:时手动模拟dealloc方法,延迟检测控制器有没有被回收。
iOS App 启动时间优化【main函数、二进制重排、PGO】
1、main函数
1.1pre-main 耗时检测
操作路径:
Edit Scheme
-- 选择一个Scheme
(比如:Run
)-- 选择Arguments
--Environment Variables
-- 点击添加 -- 设置name
:DYLD_PRINT_STATISTICS
value :${DEBUG_ACTIVITY_MODE}
Total pre-main time: 1.2 seconds (100.0%)
dylib loading time: 670.28 milliseconds (54.7%)
rebase/binding time: 46.92 milliseconds (3.8%)
ObjC setup time: 105.55 milliseconds (8.6%)
initializer time: 400.72 milliseconds (32.7%)
slowest intializers :
libSystem.B.dylib : 411015771.6 seconds (107844210.9%)
libMainThreadChecker.dylib : 43.99 milliseconds (3.5%)
libViewDebuggerSupport.dylib : 37.72 milliseconds (3.0%)
xxx : 575.47 milliseconds (47.0%)
- dylib loading :加载可执行文件(App 的.o 文件的集合), 加载动态链接库;
- rebase/binding :对动态链接库进行 rebase 指针调整和 bind 符号绑定;
- Objc setup :Objc 运行时的初始处理,包括 Objc 相关类的注册、category 注册、selector 唯一性检查等;
- initializer:包括了执行 +load() 方法、attribute((constructor)) 修饰的函数的调用、创建 C++ 静态全局变量
优化~
- 减少动态库的个数,如果太多就使用合并的方式控制,这样可以节约
dylib loading
及rebase/binding
的时间- 清理项目中未使用到类、类别等,节约
objc setup
的时间- 对于可以不在
+load
中处理的逻辑放到其他的函数中处理,如:+initialize
;控制C++
全局变量的数量;节约initializer
时间
2、二进制重排
2.1 二进制重排,主要是优化我们启动时需要的函数非常分散在各个页,启动时就会多次Page Fault
造成时间的损耗
2.2 Page Fault
进程如果能直接访问物理内存无疑是很不安全的,所以操作系统在物理内存的上又建立了一层虚拟内存。为了提高效率和方便管理,又对虚拟内存和物理内存又进行分页(Page)。当进程访问一个虚拟内存Page而对应的物理内存却不存在时,会触发一次缺页中断(Page Fault),分配物理内存
,有需要的话会从磁盘
mmap读人数据。
通过App Store渠道分发的App,Page Fault还会进行签名 验证
,所以一次Page Fault的耗时比想象的要多。
- 启动Instruments
使用Xcode选择要检测的app,Cmd+i(Profile),启动Instruments - 选择System Trace,点击Choose
这里就有疑问了,app首次打开的时候Page Fault的次数很多,打开之后再打开的话就比较少,当打开多个其他app的时候,在打开检测的app发现也会有不少Page Faults
这是由于操作系统的机制,当应用杀掉了,他所访问的物理内存不是立马就清空;它所访问的物理内存,需要通过其他app申请开辟覆盖释放掉,可以通过多打开几个应用来验证发现Page Fault次数变多了的。
2.3 重排
编译器在生成二进制代码的时候,默认按照链接的Object File(.o)
顺序写文件,按照Object File
内部的函数顺序写函数。编译器在生成二进制代码的时候,默认按照链接的Object File(.o)
顺序写文件,按照Object File
内部的函数顺序写函数。
静态库文件
.a
就是一组.o
文件的ar
包,可以用ar -t
查看.a
包含的所有.o
。
简化问题:假设我们只有两个page:page1/page2,其中绿色的method1和method3启动时候需要调用,为了执行对应的代码,系统必须进行两个Page Fault。
但如果我们把method1和method3排布到一起,那么只需要一个Page Fault即可,这就是
二进制文件重排的核心原理
。
2.4 Xcode配置Order
Xcode使用的链接器件是ld,ld有一个不常用的参数-order_file,通过man ld可以看到详细文档:
Alters the order in which functions and data are laid out. For each section in the output file, any symbol in that section that are specified in the order file file is moved to the start of its section and laid out in the same order as in the order file file.
可以看到,order_file中的符号会按照顺序排列在对应section的开始,完美的满足了我们的需求。
Xcode的GUI也提供了order_file选项:
如果order_file中的符号实际不存在会怎么样呢?
ld会忽略这些符号,如果提供了link选项-order_file_statistics,会以warning的形式把这些没找到的符号打印在日志里。
2.4.1 首先打开Write Link Map File查看
Link Map File中文直译为链接映射文件,它是在Xcode生成可执行文件的同时生成的链接信息文件,用于描述可执行文件的构造部分,包括了代码段和数据段的分布情况
我们可以在Xcode的配置中将Write Link Map File设置为YES来生成Map File
可以选中app,Show In Finder -- 找到build目录 -- 按照下面的举例的路径就可以找到:
2.4.2 将load方法设置order再看看Map File
- 新建xxx.order文件
- Xcode的Build Setting中设置Order文件
讲述了如何使用order,具体的细节、原理和实践可以参照抖音二进制重排实践;他们的数据是启动优化了15%。
3. PGO
Profile Guided Optimization简称PGO,这个也是LLVM提供的一个优化,我们可以直接在Xcode中进行配置;它是一种改进应用程序的编译器优化的方法。PGO利用应用程序的特殊工具构建来生成有关最常用代码路径和方法的配置文件信息。然后,编译器使用此配置文件信息将优化工作集中在最常用的代码上,从而利用有关程序通常如何表现的额外信息来更好地完成优化工作
配置文件引导式优化(PGO)是一项高级功能,可让您从应用中获取所有性能的最后一点点。它并不难使用,但是需要一些额外的构建步骤和一些注意事项来收集良好的配置文件信息。根据您应用程序代码的性质,PGO可以将性能提高5%到10%,但并非所有应用程序都会从中受益。如果您对性能敏感的代码需要进行额外的优化,则PGO可以提供帮助。
原理上简单说如下:
编译一个所有方法插桩了的可执行文件,
运行可执行文件(启动一次app),此时插桩的方法会把执行过的方法都记录下来,并记录方法的执行频率。例如下图的profdata文件。
重新build(原则上只是链接时需要profdata),让链接器按照profdata的信息把(启动中用到的、或者频率高的方法)放到一起。
此时这个新的可执行文件就完成了二进制文件重排
,减少了page交换(加载)的次数,提高了系统加载和运行app二进制文件的IO性能,加快了执行速度;这个相比较order的方式,这个优化还考虑了函数调用频率的问题
- Xcode配置
打开Use Optimization Profile
- 选择Action生成Profile
PGO也是官方提供的一个优化手段,具体细节可以参照苹果官方文档:Xcode Profile Guided Optimization;对于有多大的提升我这边跑了几次发现每次数据都有些出入,加上demo比较小,看着也不是很明显,感兴趣的可以在自己的项目中使用这项LLVM的优化