如何找到导致malloc“双重释放”错误的原因?

84

我正在使用Objective-C编写一个应用程序,遇到了以下错误:

MyApp(2121,0xb0185000) malloc: *** error for object 0x1068310: double free
*** set a breakpoint in malloc_error_break to debug

当我释放NSAutoreleasePool时发生了这个错误,但是我无法找出我重复释放的对象。

如何设置断点?

有没有方法可以知道这个“object 0x1068310”是什么?


4
你可能想在这篇帖子中加上iPhone标签,以便获得更多的人关注。 - Benny Wong
4
已将“iPhone”标签删除,并使用其他更相关的标签。 - Quinn Taylor
3
我无法想象为什么这个iPhone问题会缺少iPhone标签。关注“iPhone”的人一定比一些其他标签(如“autorelease”)更多。如果你想找“autorelease”,你会搜索它,而不是关注标签。所以我把“iPhone”加回去了。 - Nosredna
7
我删除“iPhone”标签的原因是因为问题与iPhone无关。仅有的联系是它出现在iPhone应用程序中,但任何C或Objective-C应用程序都可能发生完全相同的错误。我不认为追随iPhone标签的人会对此感兴趣 - 相反,可能会搜索“double free”或“malloc_error_break”等内容的人会感兴趣,即使他们加上“iPhone”,仍然会找到相关结果。让我们不要争论标签,而是考虑回答者最了解问题应该放在哪里。 - Quinn Taylor
3
这个问题至少与Cocoa相关。如果“iPhone”标签引起反感,那么可以使用“cocoa”标签。显然意图是针对XCode中的Cocoa上的Objective-C。而不是Windows、Linux或超出XCode的环境下的Objective-C。 - runako
显示剩余2条评论
13个回答

47

当一个对象被“双重释放”时,最常见的原因是您(不必要地)释放了一个自动释放的对象,并且在包含的自动释放池被清空时它稍后被自动释放。

我发现追踪额外释放的最佳方法是在Xcode中针对受影响的可执行文件使用NSZombieEnabled环境变量。有关如何使用它的快速概述,请查看此CocoaDev维基页面。(除了这个页面之外,苹果还记录了一些非常晦涩但实用的Xcode调试代码的技巧,其中一些技巧多次挽救了我的麻烦。建议在developer.apple.com上查看此技术说明 - 链接跳转到Cocoa的基础框架部分)。

编辑: 您通常可以在Xcode调试器中追踪到有问题的对象,但如果您使用Instruments工具来协助您,则通常会更容易一些。从Xcode中选择运行 → 使用性能工具启动 → 对象分配,您应该能够追踪有问题的对象,并找到它是在哪里创建的。(如果您像上面讨论的那样启用了zombie,则此方法效果最佳。)注意: Snow Leopard还向Instruments添加了一个Zombies工具,同样可以从Run菜单中访问。可能值得29美元!;-)

这里也有一个相关的SO问题。


1
CocoaDev维基页面已经失效 :( - Devarshi
使用Way Back Machine:http://web.archive.org/web/20120325135712/http://www.cocoadev.com/index.pl?NSZombieEnabled - Quinn Taylor

39

当你在调试器中打断点时,你会发现对象是什么。只需查找调用堆栈,你就能找到释放它的位置。这将告诉你它是哪个对象。

设置断点的最简单方法是:

  1. 转到“运行”->“显示”->“断点”(ALT-Command-B
  2. 滚动到列表底部并添加符号malloc_error_break

1
我尝试了,但是出现了以下错误: 无法反汇编 malloc_error_break.... 这是什么意思? - gonso
3
无法解决自动释放重复释放的问题。他需要使用Zombies工具。 - Rog
61
@gonso - 只是好奇,如果这个答案对你没有用,为什么要接受它作为答案呢? - Quinn Taylor
8
在Xcode 4.3.2中,断点可以在 View → Navigators → Show Breakpoints Navigator 或者按 ⌘6(Cmd-6)找到。 - Andreas Ley

13

除了Quinn Taylor的回答之外,我想补充我的经验。

在我的一个应用程序中,我必须解析并将数据保存到核心数据对象中,稍后获取这些对象以显示在视图上。实际上,该应用程序运行良好,根本不会崩溃,直到我尝试多次来回导航进行压力测试,尽可能快地打开多个视图时,应用程序崩溃并显示以上消息。

我尝试了Quinn在他的答案中提到的所有方法,仍然无法找到确切的原因。

我设置了NSZombieEnabled = YES和NSStackLogging = YES,并运行了命令行malloc_history以查明原因,但仍然没有运气。 它总是指向我将数据保存到核心数据对象的位置,事实上,我已经检查了那里释放的超过数量的对象数千次,没有异常。

使用各种工具(分配,泄漏等)在Instruments中运行仍然没有帮助。 启用Guard Malloc还没有发现问题。

最终的救援:我试图回到从Core Data中获取对象的视图,并向所有这些对象发送保留消息,并注意这些更改。 它解决了该问题!

所以,我发现我没有保留其中一个对象,这就是确切的原因。 只想分享我的经验,以便您为应用程序提供另一种救援方法。


11

按下 Cmd+Shift+R 组合键打开调试控制台。在那里,键入

break malloc_error_break

malloc_error_break 函数开始处设置断点。

如果您想查找位于地址 0x1068310 处的对象,您可以在调试器控制台中输入以下内容:

print-object 0x1068310
当然,在对象仍然存在的时候执行此操作--如果在执行此操作时对象已经被释放,则此方法将无效。

这是自动释放,他需要僵尸。 - Rog
1
最后我做的是在AutoreleasePoll之外调用“可疑”的方法。有趣的是,我仍然收到了警告,但没有触发断点。我只是注释掉一些块,直到找到那行代码。我正在自动释放一个使用stringWithFormat创建的字符串(没有alloc或copy)。感谢大家的提示! Gonso - gonso
对于这种特定类型的错误,断点设置在malloc_error_break上从未有助于找到问题 - 它总是需要启用zombies。 - Quinn Taylor
请参考以下链接,了解如何在XCode 4中设置malloc_error_break断点:https://dev59.com/aGw05IYBdhLWcg3w41sp - benvolioT
当我使用该命令时,出现“错误:'print-object'不是有效命令。错误:未识别的命令'print-object'。” - Daniel Ryan
1
@Zammbi:尝试使用po别名,或等效地使用expr -o。自此答案最初编写以来的这些年中,Xcode使用的调试引擎已从GDB更改为LLDB,并且LLDB具有不同的命令集。 - Adam Rosenfield

8
请按照以下步骤查找自由对象并崩溃应用程序:

1)单击“断点导航器”。
2)然后单击下面的“+”按钮。
3)从列表中添加“符号断点...”。
4)在“符号”选项上添加“malloc_error_break”关键字。

或者您也可以参考下面的GIF演示。

GIF represenation


4

对我来说,问题的解决方法是

(gdb) call (void)_CFAutoreleasePoolPrintPools()

崩溃后,栈顶的地址是罪魁祸首的地址。加入一个retain即可解决。

日志信息中给出的地址并没有帮助我找到问题所在。它从未出现在任何Instruments中。显然,这是指向已经被释放的一些内部数据的指针。


4

在Xcode 4中添加符号断点

这是关于在Xcode 4中添加符号断点的更新。

根据Xcode 4用户指南

要添加符号断点...

  1. 在断点导航器的左下角,点击“添加”按钮。
  2. 选择“添加符号断点”。
  3. 在“符号”字段中输入符号名称。
  4. 点击“完成”。

3

2

检查您的类并查看dealloc方法。确保您正在调用[super dealloc]

我曾经遇到过完全相同的问题,并发现我调用了[self dealloc]。只是没有注意。


0
如果malloc_error_break没有帮助...
解决此错误的最佳方法是打开NSZombies并使用instruments运行。当Zombie被发送消息时,Instruments将标记您,并且您可以直接跟踪回代码行。
需要Snow Leopard,但真是救命稻草!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接