使用仪器解决内存不足警告
Posted
技术标签:
【中文标题】使用仪器解决内存不足警告【英文标题】:Using Instruments to Work Through Low Memory Warnings 【发布时间】:2012-03-16 17:13:27 【问题描述】:我正在尝试使用仪器解决一些内存不足的情况。我可以在 Physical Memory Free 监视器中看到内存消耗下降到几 MB,即使 Allocations 显示 All Allocations 大约是 3 MB 而 Total Bytes 是 34 MB。
自从我使用 NSOperationQueue 将一些操作移至单独的线程后,我开始经历崩溃。但在改变之前我没有使用仪器。尽管如此,我敢打赌我做了一些我可以撤消的事情来阻止崩溃。
顺便说一句,它在不连接仪器或调试器的情况下稳定得多。
我几乎没有泄漏(崩溃前最多可能有一百个字节)。
当我查看分配时,我只看到非常原始的对象。而且它报告的总内存也很低。所以我看不到我的应用程序是如何导致这些低内存警告的。
当我从一开始就查看 Heap Shots 时,在基线和所有堆增长值的总和之间我看不到超过 3 MB。
我应该看什么才能找到问题所在?例如,我可以将它隔离到我的视图控制器实例之一吗?还是我的其他实例之一?
我做了什么: 我关闭并重新打开设备电源,这取得了显着的改进。 Instruments 未报告内存不足警告。另外,我注意到启动时的物理可用内存在重启前只有大约 7 MB,重启后大约为 60 MB。
但是,我看到物理可用内存非常规律(周期性)下降,从 43 MB 下降到 6 MB(然后又回到 43 MB)。我想知道是什么原因造成的。我没有在这个应用程序中运行任何计时器。 (我确实有一些 performSelector:afterDelay:,但在这些测试期间它们没有激活。)
我没有使用 ARC。
【问题讨论】:
您找到解决方案/解释了吗?我也有同样的问题。 【参考方案1】:allocations 和 leaks 工具仅显示对象实际占用的内容,而不显示其底层非对象结构(后备存储)占用的内容。例如,对于 UIImages,它会显示你有一些分配的字节。这是因为UIImage
对象只占用那些字节,但实际包含图像数据的CGImageRef
不是对象,在这些仪器中没有考虑到它。
如果您还没有这样做,请尝试在运行分配工具的同时运行 VM Tracker。它将让您了解正在分配的类型内存。对于 ios,此仪器显示的“Dirty Memory”通常会触发内存警告。 脏内存是VM系统无法自动丢弃的内存。如果您看到大量 CGImage,则图像可能是您的问题。
另一个重要的概念是废弃的内存。这是分配的内存,它仍然在某处引用(因此不是泄漏),但未使用。这种类型的内存的一个例子是某种类型的缓存,它不会在内存警告时释放。找出这一点的一种方法是使用堆射击分析。按下分配工具的“标记堆”按钮,做一些操作,回到应用程序中的上一点,再次按下“标记堆”。第二个堆镜头应该向您展示在这两个时刻之间分配了哪些新对象,并且可能会揭示一些谜团。您还可以重复模拟内存警告的操作,以查看该行为是否发生变化。
最后,我推荐你阅读这篇文章,它解释了这一切是如何运作的:http://liam.flookes.com/wp/2012/05/03/finding-ios-memory/。
【讨论】:
【参考方案2】:VM Tracker 中的物理内存与“Allocations”中分配的内存之间的差异是由于这些工具的工作方式的主要差异:
分配 跟踪通过在分配内存的函数(malloc
、NSAllocateObject
、...)中安装一个 tap 来跟踪您的应用程序所做的事情。这种方法产生关于每个分配的非常精确的信息,比如代码(堆栈)中的位置、数量、时间、类型。不利的一面是,如果您不跟踪以某种方式分配内存的每个函数(如vm_allocate
),则会丢失此信息。
VM Tracker 对系统虚拟内存的状态进行定期采样。这是一种不太精确的方法,因为它只是为您提供当前状态的整体视图。它以低频率运行(通常是每三秒一次),您不知道如何达到此状态。
一个已知的不可见分配的罪魁祸首是 CoreGraphics:它在解压缩图像、绘制位图上下文等时会占用大量内存。这种内存在分配工具中通常是不可见的。因此,如果您的应用处理大量图像,您可能会发现物理内存量与总体分配大小之间存在很大差异。
物理内存中的峰值可能是由于大图像被解压缩、缩小尺寸然后仅用于某些视图或图层内容中的屏幕分辨率。所有这一切都可能在UIKit
中自动发生,而无需涉及您的代码。
【讨论】:
鉴于我已经说过我已经尝试过,我仍然不清楚如何使用这些工具来追踪内存问题。虚拟机显示一条平坦的线,没有任何活动可以与可用物理内存的定期下降相关联。我尝试了其他几种工具(本质上没有所谓的“CoreGraphics”),再次发现与内存使用的脉冲没有相关性。我想将它与一个流程联系起来,但不知道我是否可以或如何去做。 -- 顺便说一句,我关闭了定位服务,这对脉动也没有任何显着影响。【参考方案3】:我几乎没有泄漏(崩溃前最多可能有一百个字节)。
根据我的经验,非常小的泄漏也是“危险”标志。事实上,我从未见过大于 4K 的泄漏,而我通常看到的泄漏是几百字节。不过,他们通常会“隐藏”在自己身后丢失的更大的记忆。
所以,我的第一个建议是:摆脱那些泄漏,即使它们看起来很小而且微不足道——其实不然。
自从我使用 NSOperationQueue 将一些操作移至单独的线程后,我开始经历崩溃。
您移动到线程的操作是否有可能导致脉冲峰值?它可以一次产生不止一次吗?
关于山峰,我认为有两种方法可以解决它们:
使用 Instruments 中的 Time Profiler 并尝试在看到峰值上升时了解正在执行的代码;
选择性地注释掉部分代码(我的意思是:应用程序的整个部分——例如,用基本/空的UIViewController
等替换“真实”控制器)并查看是否可以识别罪魁祸首。
我从未见过这样的脉动行为,所以我认为这取决于您的应用程序或您的设备。您是否尝试过使用其他设备?模拟器中发生了什么(你看到峰值了吗)?
【讨论】:
【参考方案4】:当我阅读您的文字时,我觉得您可能有一些隐藏的漏洞。我可能是错的,但您是否 100% 确定您已检查所有泄漏?
我记得几个月前我在做一个特别的项目,我遇到了同样的问题,而且 Instruments 没有泄漏。我的内存不断增长,我收到内存警告......我开始登录一些重要的 dealloc 方法。而且我已经看到一些对象,子视图(UIView)正在“泄漏”。但是它们并没有被 Instruments 看到,因为它们仍然附加在主视图上。
希望这对您有所帮助。
【讨论】:
【参考方案5】:在分配工具中,确保您选中了“仅跟踪活动分配”。见下图。我认为这可以更容易地看到实际发生的事情。
【讨论】:
【参考方案6】:您是否对项目运行过分析?如果有任何分析警告,请先修复它们。
您是否在使用任何 CoreFoundation 的东西?一些 CF 方法有......奇怪的......与 ObjC 运行时和内存管理的交互(他们不应该这样做,AFAICS,但我已经看到低级图像和 AV 操作看起来有些奇怪的行为就像 mem 在核心应用程序进程之外使用 - 也许 Apple 正在使用操作系统调用?)
... 注意:在以前的 iOS 版本中,Apple 的 CF 方法中也存在一些内存泄漏。 IIRC 最后一个已在 iOS 5.0 中修复。
-
(*** 的解析器糟透了:我输入了“3”而不是“1”)您是否正在使用大量/大型 CALayer 实例(或带有 CG* 方法的 UIView,例如 UIView 中的自定义 drawRect 方法? )
... 注意:我已经看到您描述的由上述 2 和 3 引起的 exact 行为,无论是在 CF 库中,还是在 Apple 窗口系统中尝试处理图像数据时最初是在 CF 库中生成的 - 或者是在 CALayers 中找到的。
Instruments 似乎没有正确跟踪 CA / CG 系统内的内存使用情况;这个领域有点复杂,因为 Apple 在 CPU 和 GPU 内存之间来回切换,但令人失望的是,当显然仍在使用内存时,内存使用似乎只是“消失”了!
最后的想法(4. -- 但我不让我输入那个)- 你在使用 Instruments 的不可见 RHS 吗?
Apple 硬编码 Instruments 以在您每次运行时始终禁用自身(因此您必须继续手动打开它)。这很愚蠢,因为某些核心信息仅存在于 RHS 栏中。但是我和几个甚至不知道它存在的人一起工作过:)
【讨论】:
以上是关于使用仪器解决内存不足警告的主要内容,如果未能解决你的问题,请参考以下文章