前言: 本篇文章,在于学习,我把别人的一些感觉好的文章汇总成了一篇,亲自实现了一下,留用于今后学习资料。
文章脉络:
Image may be NSFW.
Clik here to view.
文章脉络:
一、内存优化
简介:
Objective_C 有3种内存管理方法, 它们分别是
- MRR (Manual Retain Release, 手动保持释放) - ARC(Automatic Reference Counting, 自动引用计数) - GC(Garbage Collection, 垃圾收集)
- 1>MRR
① 也称为 MRC(Manual Reference Counting, 手动引用计数)
② 由程序员自己负责管理对象生命周期,负责对象的创建和销毁.- 2>ARC
① 采用和 MRR 一样的内存引用计数管理方法。
② 在编译时会在适合的位置插入对象内存释放, (如 release, autorelease, 和 retain 等),
③ 程序员不用关心对象释放的问题, 苹果推荐在新项目中使用 ARC, 但在 iOS5之前的系统中不能采用 ARC.3>GC
① 在Objective_C2.0之后, 内存管理出现了类似于 Java 和 C#的内存垃圾收集技术, 但是垃圾收集与 ARC 一直运行, 垃圾收集是后台有一个线程负责检查已经不再使用的对象,然后释放之.
② 由于后台有一个线程一直运行, 一次会严重影响性能, 这也是 Java 和 C#程序的运行速度无法超越 C++的主要原因.
③ GC 技术不能应用于 iOS 开发, 只能应用于Mac OS X 开发.小结:
从上面的介绍可知:- ① iOS 采用 MRR 和 ARC 这两种方式, ARC 是苹果推荐的方式.
- ② MRR 方式相对比较原始, 对于程序员的能力要求很高, 但是它很灵活, 方便, 很不容易驾驭好.
二、内存泄露
1> 什莫是内存泄露?
内存泄露指当一个对象或变量在使用完成后没有释放掉, 这个对象一直占用着这部分内存, 直到应用停止.2> 这种没有 释放掉的对象 多了会发生什么呢?
如果这种对象过多,内存就会耗尽,其他应用就无法运行.3> 在哪里比较普遍?
这个问题在 C++, C 和 Objective-C的 MRR 中是比较普遍的问题.4> 理论与实际?
从理论上讲, 内存泄露是由对象或变量没有释放引起的, 但实践证明并非所有的未释放的对象或变量都会导致内存泄露, 这与硬件环境和操作系统系统环境有关。5> 我们该怎么办呢?
我们需要检测工具帮助我们找到这些"泄漏点".6> 为什么要测试代码的内存泄露?
内存的泄露导致我们软件在运行过程中占用越来越多的内存,占有资源却又得不到及时清理,会导致我们程序效率越来越低,反应慢,会影响我们用户体验,失去市场的竞争能力.
三、查找泄漏点 (两种工具)
在 Xcode 中, 共提供了两种工具帮助查找泄漏点
1 > Analyze
- 学 名: 静态分析工具 - 查 找: 可以通过 Product ->Analyze 菜单项启动 - 快捷键: CMD+shift +b. - Analyze主要分析以下四种问题: 1) 逻辑错误:访问空指针或未初始化的变量等; 2) 内存管理错误:如内存泄漏等; 3) 声明错误:从未使用过的变量; 4) Api调用错误:未包含使用的库和框架。
2 >Instruments
- 学 名: 动态分析工具 - 查 找: Product ->Profile 菜单项启动 - 快捷键: CMD + i. - 简 介:它有很多跟踪模块可以动态分析和跟踪内存, CPU 和文件系统.
四、两种工具查找漏点版面的介绍
1 > 结合使用-思路分析:
先使用 Analyze 静态分析查找可疑泄漏点, 再用Instruments动态分析中的 Leaks 和 Allocations 跟踪模板进行动态跟踪分析, 确认这些点是否泄漏, 或者是否有新的泄漏点出现等.2 > 使用 静态检测内存泄漏工具 Analyze
在 Analyze 静态分析结果中, 凡是有图标
Image may be NSFW.
Clik here to view.
分析结果图标
出现的行都是工具发现的疑似泄露点.
Image may be NSFW.
Clik here to view.
疑似泄漏点所在行
点击疑似泄漏点行末尾的分叉图标,会展开分析结果:
Image may be NSFW.
Clik here to view.
展开分析结果
检测完成时的效果图如下
Image may be NSFW.
Clik here to view.
效果图
小结:
这里使用 Analyze 静态分析查找出来的泄漏点,称之为"可疑泄漏点".之所以称之为"可疑泄漏点",是因为这些点未必一定泄露,确认这些点是否泄露, 还要通过 Instruments 动态分析工具的 Leaks 和 Allocations 跟踪模板. Analyze 静态分析只是一个理论上的预测过程.3 > 动态监测Instruments的Leaks
1) CMD + i 打开
Image may be NSFW.
Clik here to view.
打开
2) 打开界面的介绍:
Image may be NSFW.
Clik here to view.
界面的介绍
在 instruments 中,虽然选择了 Leaks 模板,但默认情况下也会添加 Allocations 模板.基本上凡是内存分析都会使用 Allocations 模板, 它可以监控内存分布情况。① 选中 Allocations 模板,(图1区域),右边的3区域会显示随着时间的变化内存使用的折线图,同时在4区域会显示内存使用的详细信息,以及对象分配情况.
② 点击 Leaks 模板(图中2区域), 可以查看内存泄露情况。如果在3区域有 红X 出现, 则有内存泄露, 4区域则会显示泄露的对象.
3) 打用leaks进行监测:
点击泄露对象可以在(下图)看到它们的内存地址, 占用字节, 所属框架和响应方法等信息.打开扩展视图, 可以看到右边的跟踪堆栈信息
Image may be NSFW.
Clik here to view.
leaks进行监测
4) 监测结果的分析:
Image may be NSFW.
Clik here to view.
监测结果的分析
4 > Allocations—内存分配版面的介绍
Allocations是检测程序运行过程中的内存分配情况的,也需要同时运行着程序。界面情况如下:
Image may be NSFW.
Clik here to view.
Allocations
五、具体使用
1>.Allocations纪录了内存分配,用来优化内存使用的
2>.Leaks用来分析内存泄漏。ARC中引起的内存泄漏原因就是引用环。第一步
先选择Leaks和Leaks by Backtrace.这里可以看到那些对象内存泄漏了,泄漏了多少,这个就是简单看看,没有太多调试意义。
Image may be NSFW.
Clik here to view.
泄漏了多少
第二步
然后看看Call Tree,因为Call Tree会给我们大概的位置,有时候会给我们精确的位置,不过要看运气了。
然后,再右面选择Invert Call Tree和Hide System Library
Image may be NSFW.
Clik here to view.
Call Tree
然后双击 上文图片中的任意一行,就会跳到代码处内存泄漏的地方(事实上,到这步,很多内存泄漏的问题都会被发现),当然也有一些泄露还是看不出来的.
第三步
然后我们选择对ARC调试很有用的一个部分Circles & Roots,通过这个我们可以看到详细的ARC引用计数过程。然后,我们看到如下图小的红色矩形点击可以看到引用计数的详细信息(ARC 就是自动引用计数,计数为0,则对象会被释放)
大的红色矩形可以绘制对象引用环的图,这里如果是我们自己的东西,就能看出来各个对象之间的引用.
Image may be NSFW.
Clik here to view.
Circles & Roots
如果这里没有引用环的图. 首先我们找一下我们自定义的对象,正常完成任务这个对就应该释放的. 为了确认这个对象有没有释放, 可以重写 dealloc 方法, 在此方法中 log 释放信号, 看看是否被释放.如果这里就是没有释放,我们可以点击这儿对象后的箭头详细的看下, 这个对象的引用计数变化如图.
- All 表示所有的引用计数变化 - Unpaired表示那些为成对的变化``(成对就是leaks识别出了对应的+1,-1) - By Group会把相关的变化分成一组, - ByTime会按照顺序列出引用计数变化
Image may be NSFW.
Clik here to view.
我们选择Unpaired 和 ByGroup,看到如图
Image may be NSFW.
Clik here to view.
按照顺序看(最左边的标号)
4 这里,引用计数是一,这是正确的,因为到这里正常就是应该是OperationQueue保存一个Operation的引用。 于是,我们把正常的划掉
Image may be NSFW.
Clik here to view.
再继续看,download start 标号6和8是对应的,继续排除问题出现在这里(当然问题不可能出现在这里,这是系统的API,一定会释放,就是简单教大家如何看)
Image may be NSFW.
Clik here to view.
再看看,+1的还剩下标号7 和 11,7 是正常的为Operation分配线程,应当会+1,而11就是我们的问题所在了(大部分Delegate都不会使引用+1)。 我们再看下文档
- > @property(readonly, retain) id< NSURLSessionDelegate > delegate
原来这个代理是retain啊,不是assign或者weak。所以形成了这样的引用环。
Image may be NSFW.
Clik here to view.
那么怎么办呢?有问题下看文档,我们看到图片中引起引用计数加一的是
- > + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id<NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue:
看下文档,发现了这个地方
Image may be NSFW.
Clik here to view.
- 于是,我们要手动的去断开强引用,于是,我们手动去断开
再运行下看看,能够正常的Dealloc了.
-(void)setOperationFinished { [self.session invalidateAndCancel]; }
总结:其实大多数问题在双击上文的代码部分就可以解决了,少数问题需要详细的分析ARC引用过程。
建议
如果我们未发现表示内存泄露的红 X, 但是我们想进一步评估某个对象对于内存的应用, 可以看看 Allocations 模板的折线图. 反复执行从创建对象 -> 销毁对象 这个过程, 如果总占用内存数会随之增加, 这说明这个对象没有释放, 有些时候虽然占用的内存不是很严重, 但是也会增加占用内存, 因此必须释放这个对象.提示:有些情况下, 对象没有释放是无法检测到的,反复测试内存占用也没有明显的增加, 这时最好在配置比较低的设备上测试一下, 如果问题依然, 可以不用释放对象. 但是从编程习惯上讲, 我们应该释放该对象.
事实上,内存泄露是及其复杂的问题, 工具使用是一方面, 经验是另一方面. 提高经验, 然后借助工具才能解决内存泄露的根本.
扩展小知识
1.什莫是内存溢出 out of memory ?
是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。学习文章出处:
1. iOS_ 性能优化_内存优化_Leaks工具的使用
2. [学习笔记]_ios内存优化leaks以及timeProfiler工具的使用