1

很抱歉描述太长,但是问题并不那么容易......

我的项目在没有 GC 的情况下编写。最近我发现了一个我找不到的内存泄漏。我确实使用了新的 Xcode Analyzer,但没有结果。我确实逐行阅读了我的代码并验证了所有 alloc/release/copy/autorelease/mutableCopy/retain 和 pools... - 仍然没有。

序言:由于某种原因,Standard Instruments 和 Omni Leak Checker 对我不起作用(Omin Tool 拒绝了我的应用程序,Instruments.app(Leaks)占用了太多内存和 CPU,所以我没有机会使用它)。

所以我想编写并使用我自己的代码来挂钩和跟踪“所有”alloc/allocWithZone:/dealloc 消息统计信息,以编写一些简单的自己的泄漏检查库(主要目标只是标记可能泄漏的对象的类名)。

我使用的主要挂钩技术:

  Method originalAllocWithZone = class_getClassMethod([NSObject class],@selector(allocWithZone:));
  if (originalAllocWithZone)
  {
   imp_azo = (t_impAZOriginal)method_getImplementation(originalAllocWithZone);
   if (imp_azo)
   {
    Method hookedAllocWithZone = class_getClassMethod([NSObject class],@selector(hookedAllocWithZone:));
    if (hookedAllocWithZone)
    {
     method_setImplementation(originalAllocWithZone,method_getImplementation(hookedAllocWithZone));
     fprintf(stderr,"Leaks Hook: allocWithZone: ; Installed\n");
    }
   }
  }
  • 像这样的代码钩住 alloc 方法,而 dealloc 作为 NSObject 类别方法。

我为以前的方法实现保存 IMP,然后注册并计算所有 alloc/allocWithZone:调用为增量 (+1) stat-array NSInteger 值,而 dealloc 调用为减量 (-1)。

作为终点,我调用先前的实现和返回值。

在概念上一切正常。

如果需要,我什至可以检测类何时是类集群的一部分(如 NSString、NSPathStore2;NSDate、__NSCFDate)...通过一些规范化功能(但对于下面描述的问题无关紧要)。

但是这种技术有一些问题:

  • 并非所有类都可以被捕获,例如,[NSDate date] 在 alloc/allocWithZone 中没有捕获:但是,我可以在 GDB 中看到 alloc 调用
  • 由于我试图使用自动单例检测技术(基于retainCount readind)从最终统计信息中自动排除一些对象,当启动完整的Cocoa应用程序(实际上,甚至是简单的Objective-C命令行)时,NSLocale创建冻结在预初始化阶段包含 Foundation 框架的实用程序在 main()) 之前有一些额外的初始化 - GDB 有 allocWithZone: 一个接一个地调用,....

此处上传的完整概念项目草稿源:http: //unclemif.com/external/DILeak.zip (3.5 Kb)

Terminal.app运行make来编译它,运行./concept来展示它。


一个问题:为什么我不能通过挂钩 alloc 和 allocWithZone: 方法来捕获所有对象分配?

第二个问题:为什么挂钩 allocWithZone: 在某些类的 CFGetRetainCount (或 [inst retainCount]) 中冻结...

4

3 回答 3

6

神圣的重新发明轮子,蝙蝠侠!

你让这种方式变得比它需要的更难。绝对没有必要使用自己的对象跟踪工具(尽管这是一个有趣的心理练习)。

因为您使用的是 GC,所以用于跟踪分配和识别泄漏的工具都非常成熟。

在 GC 下,泄漏将采取以下两种形式之一;要么对早就应该被销毁的对象有强引用,要么CFRetain对象在没有平衡的情况下被'd' CFRelease

收藏家非常善于弄清楚为什么任何给定的物品都超出了它的欢迎范围。

因此,您需要找到一些停留时间过长的对象。任何物体都可以。一旦你有了所述对象的地址,你就可以使用 Instruments 中的 Object Graph 工具来找出它为什么存在;找出仍然引用它的内容或保留它的位置。

或者,从 gdb 中,使用info gc-roots 0xaddr来查找使对象成为根的所有各种事物。如果您打开 malloc 历史记录(参见 malloc 手册页),您可以获得持有该引用的对象的分配历史记录。


哦,没有GC,嗯...

您仍然有大量工具,无需重新发明轮子。

leaks命令行工具通常会给你一些很好的线索。打开 MallocStackLoggingNoCompact 以便能够使用 malloc_history(另一个命令行工具)。

或者使用 ObjectAlloc 工具。

在任何情况下,您都需要识别一个或两个正在泄漏的对象。有了它,你就可以弄清楚它上面挂着的是什么。在非 GC 中,这完全是一个弄清楚为什么它存在不被释放平衡的保留的情况。

于 2009-12-28T03:17:14.333 回答
4

即使没有 Leaks 仪器,Instruments 仍然可以为您提供帮助。

从 Leaks 模板开始,然后从中删除 Leaks 工具(因为您说它使用了太多内存)。单独的 ObjectAlloc 将告诉您所有对象的分配和解除分配,以及(打开一个选项,默认情况下在泄漏模板中)它们的所有保留和释放。

您可以将 ObjectAlloc 工具设置为仅显示仍然存在的对象;如果您将应用程序带到不应该存在任何对象(或某个类的对象)的地步,并且这些对象仍然存在,那么您就有泄漏。然后,您可以深入查找泄漏的原因。

该视频可能会有所帮助。

于 2009-12-28T04:09:13.380 回答
-2

从 Xcode 模板开始。在您知道自己在做什么之前,不要尝试为可可应用程序滚动您自己的 main() 例程。

于 2009-12-28T02:08:34.217 回答