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

标签: ios 内存泄漏 工具 | 发表时间:2016-09-20 06:39 | 作者:square10
出处:http://www.iteye.com

新款Objective-C内存泄漏自动检测工具PLeakSniffer, GitHub地址

背景

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

为什么要再造轮子

我在公司的项目里实际试用了MLeaksFinder,还查处了2处泄漏??。根据MLeaksFinder代码文件中日期推测,这个项目至少已开始半年有余,并在微信读书上得到了实践验证,在功能性和稳定性上都应该有不错的表现。

在编写完PLeakSniffer之后,查出了与MLeaksFinder相同的内存泄漏,思路迥异的代码抵达了相同的终点,写代码的乐趣莫过于此。新的思路或许还能抛砖引玉,如果激发更多的创意,也算是对iOS开发社区的一点小贡献。

MLeaksFinder现阶段能查处UIViewController和UIView的泄漏,我早先的想法还能递归的查出UIViewController之下所有Property的泄漏,并在PLeakSniffer及公司项目中得到了初步的验证,这算是对MLeaksFinder功能的一个小补充。

这类工具的意义

在我们讨论这类工具的意义之前,我们先得明确一点:

如果不使用Instrument当中的Leak检测工具,并没有什么轻易的100%精准的内存泄漏检测方式。

但这类工具还是有其存在价值的,内存泄漏的危害不用赘述,如果有一款工具能在80%的场景下检测出可能的内存泄漏,而且这种检测并不会带来任何副作用(不影响生产环境代码),为什么不使用它呢。

大部分人都低估了他们写代码时导致意外内存泄漏的可能性。Retain Cycle,Block强引用,NSTimer释放不当,这些常见的错误还是很容易出现在我们的代码里,Instrument每使用一次要费些精力,适合做定期的大排查。平常时候就更适合用MLeaksFinder,PLeakSniffer这类工具来做实时监控,提供免费建议。

PLeakSniffer实现思路

我们绝大部分时候都是在编写UIViewController,UIViewController就像一个根节点,持有并管理着很多的子节点对象,这些子节点的生命周期都依赖于Controller,Controller释放的时候,他们也随之释放。用一张图简单的描述他们的关系:

sniffer1.jpg

根据各个应用使用的设计模式不同(MVC,MVP,MVVM等),Controller所持有的Property也不相同。这里我们使用MVP作为例子,Controller所包含的对象就包括各种View对象,和Presenter,Model对象。当然每个对象又有可能持有更多的子对象。

PLeakSniffer基于这样一个假设:

如果Controller被释放了,但其曾经持有过的子对象如果还存在,那么这些子对象就是泄漏的可疑目标。

当然这个假设并不是一个100%适用的真理,不同工程师编写代码的方式风格差别很大,有些会把某些UIViewController做成单例(个人觉得这不是个好主意。。),有些会把某些View缓存起来(即使Controller已被释放),还会有其他考虑不到的场景。但在80%以上的场景,我们在Controller结束生命周期之后会将其持有的资源一并释放。这时候PLeakSniffer可以发挥用处,给你一些免费的泄漏建议。

那么怎么在Controller被释放之后,知道其持有的对象没有被释放呢?

一个小技巧可以达成这个目标:子对象(比如view)建立一个对controller的weak引用,如果Controller被释放,这个weak引用也随之置为nil。那怎么知道子对象没有被释放呢?用一个单例对象每个一小段时间发出一个ping通知去ping这个子对象,如果子对象还活着就会一个pong通知。所以结论就是:如果子对象的controller已不存在,但还能响应这个ping通知,那么这个对象就是可疑的泄漏对象。完整的结构可以用下图表示:

sniffer2.jpg

通知移除需要一个时机,这里我们使用Associated Object机制给每一个子对象再生成一个Proxy对象,在Proxy对象的dealloc里面移除通知。

当然什么时候去判断一个对象的生命周期开始,什么时候判断为结束,需要一个精挑细选的机制。View,Controller,Property各不相同。

PLeakSniffer采取保守的策略,通过Objective C的runtime机制,递归的将一个Controller所有强引用的property找出,并安装proxy监听Ping通知。在我的测试下,基本上能将property泄漏的场景找出。

PLeakSniffer的使用方式很简答,通过Pod安装后,通过以下代码激活即可。

1
2
3
4
#if MY_DEBUG_ENV
[[PLeakSniffer sharedInstance] installLeakSniffer];
[[PLeakSniffer sharedInstance] addIgnoreList:@[@ "MySingletonController" ]];
#endif

addIgnoreList可以添加一些特殊的忽略名单,比如单例这种无法正确预测泄漏的对象。切记用Debug的宏将上述代码包住,不要把这些检测泄漏的代码带进线上环境。

如果检测到可疑泄漏,PLeakSniffer会在控制台打印一条日志:

Controller泄漏:Detect Possible Controller Leak: %@

其他对象泄漏:Detect Possible Leak: %@

更多的细节请查阅代码: GitHub地址



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [ios 内存泄漏 工具] 推荐:

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

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

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

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

linux实用技巧:检测内存泄漏工具Valgrind

- - CSDN博客系统运维推荐文章
1.Valgrind简介.         1.在我们编写C/C++的时候,最常见的错误之一就是内存泄露,这样的问题其实跟程序员的编程习惯密不可分. 如果你在申请内存空间之后能够马上在合适的位置释放内存. 会极大程度的避免内存泄漏的情况. 有时候我们确实忘记释放内存而导致了比较严重的错误,所以这个时候我们需要借助valgrind这个工具来判断内存泄漏.

内存泄漏检查工具valgrind的安装与使用

- - CSDN博客推荐文章
Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件工具套装. 它最流行的工具是Memcheck, 它能检测C/C++中大部分的内存相关的错误. 程序编译时使用 “-g”参数,以添加调试信息,这样Memcheck的错误消息可以精确到行;. 编译时使用“-O0”也有必要,只是速度会很慢,“-O1”可能会导致Memecheck的错误消息不正确;.

Google开源Leak Finder——用于检测内存泄漏的JavaScript工具

- - InfoQ cn
近日,Google开源了 Leak Finder,这款工具可以查看JavaScript应用的堆,进而发现内存泄漏. 作为一门垃圾收集语言,JavaScript并不会出现常见的内存泄露情况,特别是像C++等语言中所见到的那种. 但如果依旧将内存分配给那些不再使用的对象,那么应用就会出现内存泄漏;如果泄漏情况很严重,那么应用的性能就会受到影响,甚至无法执行.

内存泄漏

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

java内存泄漏

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

浅谈Java--内存泄漏

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

Android 解析内存泄漏

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

C/C++的内存泄漏检测工具Valgrind memcheck的使用经历(求大神解答疑惑,找出内存泄露真凶)

- - CSDN博客综合推荐文章
Linux下的Valgrind真是利器啊(不知道Valgrind的请自觉查看参考文献(1)(2)),帮我找出了不少C++中的内存管理错误,前一阵子还在纠结为什么VS 2013下运行良好的程序到了Linux下用g++编译运行却崩溃了,给出一堆汇编代码也看不懂. 久久不得解过后,想想肯定是内存方面的错误,VS在这方面一般都不检查的,就算你的程序千疮百孔,各种内存泄露、内存管理错误,只要不影响运行,没有读到不该读的东西VS就不会告诉你(应该是VS内部没实现这个内存检测功能),因此用VS写出的程序可能不是完美或健壮的.