一步步调试解决iOS内存泄漏

标签: General | 发表时间:2014-12-06 06:00 | 作者:ajaxj
出处:http://www.geek521.com

虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在。所以了解原理很重要。

这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled设置的使用。

本文假设你已经比较熟悉Obj-C的内存管理机制。

实验的开发环境:XCode 4.5.2

1、运行Demo

先下载一个实现准备好的内存泄露的Demo吧: leak app

下载下来,打开运行,程序是一个寿司的列表,列出各种寿司卷。试着选择里面的几行,应该是选第二行的时候就崩溃了。崩溃截图:

在崩溃的地方断住了,知道crash的地方了,但是不知道具体crash的原因。

2、设置NSZombieEnabled

这是一个 “EXC_BAD_ACCESS”错误。我们打开XCode的选项:“NSZombieEnabled” 。在crash时可能会给你更多的一些提示信息。

设置步骤:1

2:勾上红色框里的

运行,按刚才的操作选中其中的cell。再次crash,这次在output窗口会看到多了一项错误信息:

2012-11-28 13:22:08.911 PropMemFun[2132:11303] *** -[CFString respondsToSelector:]: message sent to deallocated instance 0x713ebc0

大概意思是:向已释放的内存发送消息。也就是说使用了已释放的内存,在C语言相当于使用了“野指针”

看了下crash的这个语句,sushiString应该是没问题的,它是从stringWithFormat初始化出来的。那就是_lastSushiSelected的问题。

_lastSushiSelected指向了sushiString,sushiString是一个autorelease变量。 在第二次点击时,使用的是sushiString已经被释放,所以crash了。那为_lastSushiSelected保留一下,就可以用了。代码修改如下:

 _lastSushiSelected = [sushiString retain];

运行,这时候不崩溃。

3、分析内存泄露(shift+command+b)

app不crash了,那看看有没有内存泄露。用XCode的Analyze就能分析到哪里有内存泄露

分析之后可以看到:

这里提示alertView没被释放,有内存泄露,那我们释放

[alertView release];

再分析,这个问题解决了。

4、使用Instruments的leaks工具

分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的。那就需要用到Instruments了。
按上面操作,build成功后跳出Instruments工具,选择Leaks选项,这时候寿司程序也运行起来了,选中list中的项,拖动等操作后,工具显示效果如下:
大家可能都能猜到,红色的柱子表示内存泄露了。怎么通过这个工具看到在哪泄露了呢?
先在工具栏按下红色的圆形按钮,把工具监视内存的活动停下来。选择Leak,然后点中间十字交叉那,选择Call Tree.
这时候左下角的Call Tree的可选项可以选了。选中Invert Call Tree 和Hide System Libraries,显示如下:
这时候内存泄露的具体代码找到了,在右边的红色框框里指定了哪个方法出现了内存泄露。
你只要在这些方法上双击,就会跳转到具体的代码,哈哈,是不是很方便。
这里应该是提示100%内存会泄露。

6、解决内存泄露问题

问题找到了,那就解决吧

关于:tableView:didSelectRowAtIndexPath ,分析下它的内存过程:

  1. sushiString变量通过autorelease创建,它的引用计数是1.
  2. 这行代码使得引用计数增加到2, _lastSushiSelected = [sushiString retain];
  3. 这个方法结束时,sushiString的autorelease生效了,这个变量的引用计数减少为1
  4. 当再次执行tableView:didSelectRowAtIndexPath这个方法时,_lastSushiSelected被赋值了新指针,老的_lastSushiSelected的引用计数还是1,没有被释放,产生了内存泄露。

怎么解决呢?

在_lastSushiSelected = [sushiString retain];之前把原来的release就ok了:

[_lastSushiSelected release];
    _lastSushiSelected = [sushiString retain];

关于:tableView:cellForRowAtIndexPath

这个比较明显,sushiString被alloc和init之后就没有释放,可以用stringWithFormat来调用autorelease,代码如下:

NSString *sushiString = [NSString stringWithFormat:@"%d: %@", indexPath.row, sushiName];

好了,泄露都fix了,再用工具分析看看,这时候你再点,再拖,再怎么操作,都没有内存泄露了。表明内存泄露被堵住了。

相关 [调试 ios 内存泄漏] 推荐:

一步步调试解决iOS内存泄漏

- - 极客521 | 极客521
虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在. 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled设置的使用. 本文假设你已经比较熟悉Obj-C的内存管理机制. 实验的开发环境:XCode 4.5.2.

iOS内存泄漏自动检测工具PLeakSniffer

- - 移动开发 - ITeye博客
新款Objective-C内存泄漏自动检测工具PLeakSniffer, GitHub地址. 前些天读到WeRead团队分享的一款内存泄漏检测工具 MLeaksFinder,恍惚想起早些时候自己也有过编写这样一个小工具的想法,不知道由于什么原因把这事给忘记了. 在仔细读过MLeaksFinder源码,了解实现思路之后,发现和自己最初的想法并不相同,终于在上个周末战胜拖延症将之前的想法付诸于代码,也就诞生了这款功能类似的内存泄漏检测工具PLeakSniffer.

内存泄漏

- - CSDN博客系统运维推荐文章
程序申请了堆空间,但是“忘记”释放,导致该块区域在程序结束前无法被再次使用导致的. 泄漏时间长了,就会导致用户空间内存不足,严重的导致死机. 如果泄漏比较严重,很容易察觉;但是有些泄漏很缓慢,不容易察觉,但是软件会运行很长时间后,会慢慢导致严重问题,而且当发现症状的时候,基本上已经是比较晚的时候了,想要识别泄漏,还是可以实现的,本篇文章来聊聊内存操作的原理.

java内存泄漏

- - 编程语言 - ITeye博客
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址. Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的. GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.

浅谈Java--内存泄漏

- - ITeye博客
      JAVA的垃圾回收机制,让许多程序员觉得内存管理不是很重要,但是内存内存泄露的事情恰恰这样的疏忽而发生,特别是对于Android开发,内存管理更为重要,养成良好的习惯,有利于避免内存的泄漏..     这里可以把许多对象和引用看成是有向图,顶点可以是对象也可以是引用,引用关系就是有向边.

Android 解析内存泄漏

- - CSDN博客移动开发推荐文章
1、引用没释放造成的内存泄露.        1.1、注册没取消造成的内存泄露.        这种 Android的内存泄露比纯 Java的内存泄露还要严重,因为其他一些Android程序可能引用我们的Anroid程序的对象(比如注册机制). 即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄露的内存依然不能被垃圾回收.

Perfdog玩转内存泄漏

- - 操作系统 - ITeye博客
最近QC同学在跑游戏的过程中发现玩的时间久了游戏会发生闪退,经过搜集信息后排除了功能性bug的. 拿到真机,USB连接,杀掉多余后台进程,打开Perfdog,接下来一顿操作猛如虎,Perfdog具体操作不在赘述,有关perfdog怎么使用的教程可以参考. 此图一出,基本就可以断定内存泄露了,这是正常玩游戏,游戏运行了30分钟的内存趋势图;.

IOS上objective-c开发调试方法

- - 瞄的技术
常用总结,陆续补充,免得忘记. 1.如果问题是可以复现的,用Breakpoint可以跟踪出错位置在进行分析. 2.如果使用Breakpoint无法查出crash问题,问题无法复现,可以用profile记录运行过程中的内存,cpu使用,看是否在某一功能突然升高,不稳定. 3.可以通过将所有的NSLOG控制台输出截获到文件输出,在真机上运行来排查在模拟器上无法复现的问题.

shared_ptr真能防止内存泄漏吗?

- Roger - codedump
这个命题有些诡异,因为shared_ptr设计的初衷就是为了防止内存泄漏,但是先别急,等我把问题描述清楚.. 事出缘由是这几天项目出现一个内存泄漏的bug,之前这部分是使用shared_ptr封装了很多指针的操作,后来出于效率的考虑,改回了裸指针.由于我们使用的google tcmalloc做内存分配,它自带了检测内存泄漏的功能,于是在单元测试的时候就被检查出了内存泄漏..

(转)ThreadLocal的内存泄漏问题

- - 编程语言 - ITeye博客
原文:http://www.godiscoder.com/?p=479. 在最近一个项目中,在项目发布之后,发现系统中有内存泄漏问题. 表象是堆内存随着系统的运行时间缓慢增长,一直没有办法通过gc来回收,最终于导致堆内存耗尽,内存溢出. 开始是怀疑ThreadLocal的问题,因为在项目中,大量使用了线程的ThreadLocal保存线程上下文信息,在正常情况下,在线程开始的时候设置线程变量,在线程结束的时候,需要清除线程上下文信息,如果线程变量没有清除,会导致线程中保存的对象无法释放.