因文章单篇过长,依照 原理、剖析东西 和 实战 拆分红上、中、下三部分,点击阅读。
- iOS内存管控实战(上)—原理篇
- iOS内存管控实战(中)-剖析东西篇
- iOS内存管控实战(下)—实战篇
前言
近期有个搭档遇到了内存走漏导致卡死的问题,详细原因是:view A监听了某个事情,但viewA自身由于block运用不当,呈现了走漏问题,用户多次操作导致viewA走漏了72次。当事情发出告诉时,72个被走漏的viewA一起去触发某个操作,导致手机卡死。这个问题挺经典的,平常假如不注意 block weakify/strongify ,内存走漏真呈现时,影响颇大。
加上有搭档在debug内存走漏时秀了一把 Xcode memory Debug,让我觉得是时分将过去所学的内存办理相关的常识进行一番汇总,本篇文章更多的作业在于汇总和黏连各个优异的文章,假如您有精力,主张仍是阅读文章结尾粘贴的原文链接,在此也感谢长辈们的共享。
本篇文章目录为:
- 一、iOS内存根底原理
- (一)iOS体系内存上限
- (二)内存走漏的三种类型
- (三)Clean & Dirty & Compressed 内存
- 二、内存剖析东西
- (一)剖析东西一览
- (二)Xcode memory debugger 和 指令操作
- (三)Facebook循环引证检测结构:FBRetainCycleDetector
- (四)轻量级的内存走漏检测结构:MLeaksFinder
- 三、内存优化实战
- (一)图片紧缩优化
一、iOS内存根底原理
(一)iOS体系内存上限
在 stack overflow 上,有人对单个 App 能够运用的最大内存做了统计:iOS app max memory budget。以 iPhone XS Max 为例,一共的可用内存是 3735 MB(比硬件大小小一些,由于体系自身也会消耗一部分内存),而单个 App 可用内存到达 2039 MB,到达了 55%。当 App 运用的内存超过这个临界值,就会发生 OOM 崩溃。能够看出,单个 App 的可用物理内存实际上仍是很大的,要发生 OOM 崩溃,绝大多数情况下都是程序自身出了问题。
OOM常见原因:
- 内存走漏:最常见的原因之一便是内存走漏。
- UIWebview 缺点:无论是翻开网页,仍是履行一段简略的 js 代码,UIWebView 都会占用很多内存,一起旧版本的 css 动画也会导致很多问题,所以最好运用 WKWebView。
- 大图片、大视图:缩放、绘制分辨率高的大图片,播放 gif 图,以及烘托自身 size 过大的视图(例如超长的 TextView)等,都会占用很多内存,轻则形成卡顿,重则或许在解析、烘托的过程中发生 OOM。
实战阐明:最近我在优化图片宣布的逻辑,debug发现在选择多张超高清大图进行紧缩时,内存占用甚至会飙升到2G,依照 stack overflow 上的说法,2G现已触碰OOM红线了,所以我对图片紧缩的逻辑进行了优化,感兴趣的能够到我博客检查最新的一篇文章。
(二)内存走漏的三种类型
先看看 Leaks,从苹果的开发者文档里能够看到,一个 app 的内存分三类:
- ❎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.
- ✅Cached memory: Memory still referenced by your application that might be used again for better performance.
其间 Leaked memory 和 Abandoned memory 都属于应该开释而没开释的内存,都是内存走漏,而 Leaks 东西只担任检测 Leaked memory,而不管 Abandoned memory。在 MRC 年代 Leaked memory 很常见,由于很容易忘了调用 release,但在 ARC 年代更常见的内存走漏是循环引证导致的 Abandoned memory,Leaks 东西查不出这类内存走漏,运用有限。
(三)Clean & Dirty & Compressed 内存
1. Pages Memory
内存是由体系办理,一般以页为单位来划分。在 iOS 上,每一页包含 16KB 的空间。一段数据或许会占用多页内存,所占用页总数乘以每页空间得到的便是这段数据运用的总内存。
内存页依照各自的分配和运用状态,能够被分为 Clean 和 Dirty 两类。
以上面的代码为例,请求一块长度为 80000 字节的内存空间,依照一页 16KB 来计算,就需求 6 页内存来存储。
- 当这些内存页开辟出来的时分,它们都是 Clean 的
- 当向处于第一页的内存写入数据时,第一页内存会变成 Dirty
- 当向处于最后一页的内存写入数据时,这一页也会变成 Dirty
2. 内存映射文件
当 App 访问一个文件时,体系内核会担任调度,将磁盘上的文件加载并映射到内存中。假如这是只读的文件,它所占用到的内存页是 Clean 的。
如下图所示,一个 50KB 的图片被加载到内存中时,需求分配 4 页内存来存储。其间第四页中有 2KB 的空间会被用来存储这个图片的数据,剩下空间或许会被用来存储其它数据。
3. 典型app内存类型
当内存不足的时分,体系会依照一定策略来腾出更多空间供运用,比较常见的做法是将一部分低优先级的数据挪到磁盘上,这个操作称为 Page Out。之后当再次访问到这块数据的时分,体系会担任将它重新搬回内存空间中,这个操作称为 Page In。
可是对于移动设备而言,频繁对磁盘进行IO操作会降低存储设备的寿数。从 iOS7 开端,体系开端采用紧缩内存的办法来开释内存空间,被紧缩的内存称为 Compressed Memory。下面依次介绍一下 iOS App 通常情况下的三种内存类型:Clean Memory 、Dirty Memory以及Compressed Memory。
4. Clean Memory
Clean Memory 是指那些能够用以 Page Out 的内存,包含已被加载到内存中的文件,或许是 App 所用到的 frameworks。每个 frameworks 都有 _DATA_CONST 段,当 App 在运行时运用到了某个 framework,它所对应的 _DATA_CONST 的内存就会由 Clean 变为 Dirty。
5. Dirty Memory
Dirty Memory 是指那些被 App 写入过数据的内存,包含一切堆区的目标、图画解码缓冲区,一起,类似 Clean memory,也包含 App 所用到的 frameworks。每个 framework 都会有 _DATA 段和 _DATA_DIRTY 段,它们的内存是 Dirty 的。
值得注意的是,在运用 framework 的过程中会发生 Dirty Memory,运用单例或许大局初始化办法是削减 Dirty Memory 不错的办法,由于单例一旦创立就不会毁掉,大局初始化办法会在 class 加载时履行。
6. Compressed Memory
当内存吃紧的时分,体系会将不运用的内存进行紧缩,直到下一次访问的时分进行解压。
例如,当咱们运用 Dictionary 去缓存数据的时分,假定现在现已运用了 3 页内存,当不访问的时分或许会被紧缩为 1 页,再次运用到时分又会解压成 3 页。
官方对内存紧缩的描绘是:
With OS X Mavericks, Compressed Memory allows your Mac to free up memory space when you need it most. As your Mac approaches maximum memory capacity, OS X automatically compresses data from inactive apps, making more memory available.
大致上是在内存不够用的时分,把非活跃运用占用的内存进行紧缩。能够看出相对于把dirty的内存换出到硬盘而言,这是一种折中的计划,本质上是用CPU时间换硬盘I/O时间。虽然紧缩/解压会比换出/换入占用更多的CPU,但花在硬盘I/O上的时间会大大减小。
7. Memory Warnings
并非一切内存正告都是由 App 形成的,例如在内存较小的设备上,当你接听电话的时分也有或许发生内存正告。依照以往的习气,你或许会在收到内存正告告诉的时分去做一些开释内存的事情。可是内存紧缩机制会使事情变得复杂。咱们来看看这个例子:
假定代码中的 cache 已被紧缩过
事实上,当你尝试去再次访问 cache 目标的时分,体系会先解压这块内存
这个过程中内存运用会增加,在内存吃紧的时分,这并不是咱们想要的。随后,当咱们会履行很多作业去清空 cache,最终得到的内存空间和内存紧缩的成果相同
所以,相比以往的缓存手法,更加主张去调整策略,例如削减缓存运用,或许在收到内存正告的时分,将这类事情交由体系去处理。
8. Caching
咱们对数据进行缓存的目的是想削减 CPU 的压力,可是过多的缓存又会占用过大的内存。由于内存紧缩机制的存在,咱们需求根据缓存数据大小以及重算这些数据的本钱,在 CPU 和内存之间进行权衡。
在一些需求缓存数据的场景下,能够考虑运用 NSCache 代替 NSDictionary,由于 NSCache 能够自动整理内存,在内存吃紧的时分会更加合理。
9. 小结
通常情况下,咱们所说的内存占用是指 Dirty Memory 和 Compressed Memory,Clean Memory 不需求过多关怀。
参阅文章
- WWDC 2018:iOS 内存深入研究
- iOS调试Block引证目标无法被开释的一个小技巧
- iOS之深入解析Memory内存
- MLeaksFinder:精准 iOS 内存走漏检测东西
- OS X Mavericks 中的内存紧缩技能到底有多强壮?
- FBRetainCycleDetector + MLeaksFinder 阅读
- 获取OC目标的一切强引证目标
- WWDC2018 图画最佳实践
——————————————
文章首发:问我社区