[toc] ## 前言 根据内存区块的引用状态划分内存: - `缓存内存(Cached memory)` - `泄漏内存(Leaked memory)` - `废弃内存(Abandoned memory)` - `僵尸内存(Zombies memory)` `缓存内存`:正常使用的内存 `泄漏内存`:`没有引用也没有被释放的内存`。可以用`leaks`检测到,MRC常见,ARC多为Core相关库未release导致 `废弃内存`:`内存仍存在引用,但是无法被使用到`。无法用leaks检测到,可尝试使用`Allocations`排查,一般为`循环引用导致` ## 一、内存分类 根据`内存区块`的引用状态,可以把内存分为缓存内存(Cached memory)、泄漏内存(Leaked memory)、废弃内存(Abandoned memory)和僵尸内存(Zombies memory),其中`泄漏内存`和`废弃内存`为`内存增长`的主要原因,`僵尸内存`的使用会导致程序的`crash` >Cached memory: Memory still referenced by your application that might be used again for better performance. > >Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument). > >Abandoned memory: Memory still referenced by your application that has no useful purpose. Zombies: objects that are called after they’ve been released and no longer exist `1.泄漏内存`:内存没有被释放,也没有对应的引用,可以被Leaks工具检测到。 常见情形:MRC中Retain没有对应的Release,ARC中一般出现这种情况多为`c相关对象`未调用相对应的`release`方法 ```objectivec 示例1: NSString * testStr = (__bridge NSString *)(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)[NSString stringWithFormat:@"%@",self],NULL,CFSTR("!*'();:@&=+$,/?%#[]"),kCFStringEncodingUTF8)); // CFRelease(testStr); 示例2: CGImageRef imageRef = CGImageCreateWithImageInRect([[UIImage imageNamed:@"xxx"] CGImage], CGRectZero); // CFRelease(imageRef); ``` `2.废弃内存`:`内存没有有效的引用,无法被程序使用`,无法用Leaks检测到,可以通过Allocations分析出 常见情形:主要为循环引用和常驻对象强持有,如delegate声明为强引用,属性block强持有外部变量导致的循环引用,NSTimer未及时释放等 ```objectivec 实例1: self.callback = ^ { NSLog("%@",self); } 实例2: self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(task) userInfo:nil repeats:YES]; // [self.timer invalidate]; // 需要注意的是如果对象由于Timer被runloop强引用时,在该对象的dealloc中是停止定时器是不生效的,因为由于强引用导致该对象不会被释放,dealloc便不会被调用 ``` `3.僵尸内存`:`对象内存已经被释放,仍存在对应的引用指向该部分区域`。通过引用使用该部分区域可能会造成`EXC_BAD_ACCESS`异常 常见情形:已经释放的对象执行release或收到其他的调用;对象创建后没有被强引用却被使用 ## 二、内存泄漏排查 **1.1 静态分析** 通过`Xcode Analyze`功能静态分析代码,选中Xcode工程菜单 `Product - Analyze`,静态分析可以分析出程序无用变量,无法执行到的分支,废弃接口,静态内存泄漏等 >注:cocoapods debug切换到release需要使用pod install,否则有可能无法生成release.xcconfig ![](https://img.kancloud.cn/53/0e/530e46166ca482e72e3383a78b2b01e9_1200x884.png) **1.2 Runtime分析 Leaks&Allocations** 通过Xcode `Leak`工具运行时分析代码 Xcode选中`Product - Profile`,以Profile方式安装程序到真机,打开Instruments中的Leaks,选中左上角的录制按钮,选中对应程序开始执行分析 **常用设置:** `Call Tree`:调用栈方式查看泄漏来源 `Invert Call Tree`:反转调用堆栈,可以直接把泄漏的调用处显示在最顶层 `Hide System Libraries`:隐藏系统库相关符号,由于没有符号化信息,建议隐藏 `Automatic Snapshotting`:自动扫描,扫描一次耗时较大,大客户端建议关闭自动扫描或者增加扫描间隔,或使用手动进行snapshot,否则可能会卡顿和程序异常退出 `Snapshot Now`:立马扫描生成一次内存快照 ![](https://img.kancloud.cn/09/e6/09e6915e573033c0650330747458affa_1200x503.png) ------------- ![](https://img.kancloud.cn/58/ab/58ab9549b8281632331bf908c4222db6_1200x547.png) ---------- ![](https://img.kancloud.cn/58/ab/58ab9549b8281632331bf908c4222db6_1200x547.png)) >注:如果无法显示符号化后的堆栈信息,需要查看工程配置中是否在Release下生成对应的dsym ![](https://img.kancloud.cn/3a/b8/3ab833041354e50e29d8882bcdb9e591_1200x418.png)