当泄漏工具没有显示内存泄漏时,如何调试内存泄漏?

我有一个用 Swift 编写的 iOS 应用程序正在泄漏内存——在某些情况下,一些对象应该被释放,但它们没有。我通过简单地添加如下 deinit调试消息了解到了这个问题:

deinit {
println("DEINIT: KeysProvider released")
}

因此,Deinit 消息应该在导致对象释放的事件之后出现在控制台中。但是,对于应该释放的一些对象,消息丢失了。尽管如此,泄漏开发工具没有显示任何泄漏。我该怎么解决这种情况?

25627 次浏览

使用工具检查由于保留但未泄漏内存而导致的泄漏和内存丢失。后者是仍然指向的未使用内存。在仪器分配工具中使用标记生成(Heapshot)。

有关如何使用 Heapshot 查找内存创建,请参见: Bbum 博客

基本上,这种方法是运行 Instruments 分配工具,拍摄一个大型照片,运行代码的迭代,然后重复拍摄另一个大型照片3到4次。这将指示在迭代期间分配而没有释放的内存。

计算结果以查看各个分配。

如果您需要查看对象使用工具的保留、释放和自动释放位置:

在仪器中运行,在分配中设置“记录参考计数”(对于 Xcode 5或更低,必须停止记录才能设置选项)。使应用程序运行,停止录制,向下钻取,你将能够看到所有的保留,释放和自动释放发生的地方。

在 Xcode 8中,你可以点击“ Debug Memory Graph”按钮,调试工具栏中的 图片来源: img src = “ https://i.stack.imgur.com/JFx6Z.png”alt = “ debug gmemory ygraph 按钮”/> (显示在屏幕底部) :

debug memory graph

参见苹果的 诊断和解决运行应用程序中的错误: 可视化并诊断增加的内存使用

只需在左边面板中标识您认为应该释放的对象,它就会显示对象图(如上面的主画布所示)。这对于快速识别在有关对象上建立强引用的位置非常有用。从这里,你可以开始你的研究,诊断为什么那些强引用没有被解析(例如,如果有问题的对象有一个强引用来自其他应该被释放的对象,也看看那个对象的图,你可能会发现问题(例如,强引用循环,重复计时器,等等)。

注意,在右边的面板中,我看到了调用树。我是通过在方案设置中打开“ malloc 栈”日志记录选项得到这个结果的:

malloc stack

无论如何,做完这些之后,你可以点击上面第一个屏幕快照的右边面板中堆栈跟踪中相关方法调用旁边的箭头,你就可以看到这个强引用最初建立的地方:

code


在我的原始答案中,下面描述了传统 Instruments 技术(如果使用较早版本的 Xcode,这种技术尤其有用)。


我建议使用 Instruments 的“分配”工具和“记录参考计数”功能:

record reference counts

然后,你可以在 Instruments 中运行这个应用程序,然后搜索你知道正在泄漏的类,点击箭头进行钻取:

enter image description here

然后,您可以深入了解详细信息,并使用右边的“ Extended Details”面板查看堆栈跟踪:

extended details

在“ Extended Details”面板中,将焦点放在黑色的代码上,而不是放在灰色的系统调用上。无论如何,从“扩展细节”面板中,你可以钻入你的源代码,就在 Instruments: :

your code

有关使用 Instruments 跟踪内存问题的更多信息和示范,请参阅:

  • WWDC 2021视频
  • WWDC 2019视频 仪器入门
  • WWDC 2018视频
  • WWDC 2013年视频
  • WWDC 2012视频