由于 CoreFoundation 框架导致的内存泄漏

Posted

技术标签:

【中文标题】由于 CoreFoundation 框架导致的内存泄漏【英文标题】:Memory leaks caused due to CoreFoundation Framework 【发布时间】:2011-01-10 14:12:44 【问题描述】:

我正在开发一个主要使用地址簿和数据库的 iPhone 应用程序。从通讯录中获取大约 3000 个联系人后,我将字符串标签附加到联系人(每个 5 个)。我正在将我的标签保存在数据库中。

出于负载测试的目的,我在应用程序中添加了 10,000 个标签。但是在我的应用程序的负载测试期间,我观察到一些与应用程序代码无关但代表一组指令集的内存泄漏。 Instruments 还显示 Foundation 是泄漏的负责库(广泛使用属于 Foundation 框架的NSStringNSDictionaryNSArray)。我的应用程序在使用 10 到 15 分钟后崩溃。崩溃报告中提到,应用程序由于内存不足而崩溃。

使用 CLANG 的内存分析显示零泄漏。我如何解决这些内存泄漏? 这些泄漏是坠机背后的真正罪魁祸首吗?是否有任何其他工具可用于检查内存泄漏?

【问题讨论】:

这可能是一个自动释放问题。如果你分批做,它仍然会发生吗?也就是说,在运行循环的不同迭代中?也许用它自己的池来包装操作。 是的,一旦应用程序收到内存警告,我尝试排空自动释放池,但排空池也没有好处。 您应该知道泄漏是否是罪魁祸首,Instruments 说您泄漏了多少内存? 仪器显示高达 25 MB 的实时字节然后崩溃,有时它仅在 12 MB 的实时字节后崩溃。 它是明确的泄漏吗?或者更确切地说是分配问题(例如,您使用了太多内存)? 【参考方案1】:

我经常发现我的漏洞说它们是由 Core Foundation(或与此相关的任何其他框架)引起的,但实际上是我自己的。除了模拟器之外,您很少会发现框架中有过多的泄漏。

如果您在 Instruments 中打开右侧的详细信息面板,您可能会发现其中列出了您应用程序的方法。这将告诉您它可能来自您的代码中的何处。一次泄漏会引发许多其他泄漏,您可能必须找到***别的罪魁祸首才能摆脱较低级别的泄漏。

除了发现最明显的漏洞外,您不应该期望 Clang 做任何事情。它非常方便,但仅此而已,只是对编译的有用补充。

【讨论】:

【参考方案2】:

clang 不是泄漏检查器。它只检测一小部分问题。

对于内存泄漏调试,您应该关注仪器,特别是对象分配和泄漏仪器。不过,请务必了解泄漏与其他高内存使用源之间的区别。

一旦您确定对象正在泄漏,请使用 Instruments 检查它们的分配堆栈跟踪(这样您就可以知道它是什么对象)以及它们的保留/释放历史记录。

如果不是泄漏,那么我建议调查这里的说明:http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using -heapshot-analysis-to-find-undesired-memory-growth/

【讨论】:

是的,我已经解决了由于框架引起的所有泄漏,我认为是由于框架引起的,但不是......所以现在问题出在分配上......有很多分配在应用的生命周期内完成,导致应用在使用 10 分钟后崩溃。【参考方案3】:

您很可能有创建基础对象的代码。泄漏向您显示分配的位置,但这通常是由于调用您的代码来创建对象。你可以查看 Instruments 中的调用链,然后沿着调用链返回,直到你找到你的代码——那是你导致分配的地方。现在,对于那个分配,看看你对那个对象的内存处理:你会在一段时间后释放它吗?

有很多方法可能会导致您无法释放内存属性,因此很难猜测您可能会遇到哪一种。我在帮助人们时看到的包括分配对象并通过具有保留属性的属性将其分配给实例变量,如下所示:

@property(保留)NSString* myString;

...

self.myString = [[NSString alloc] initWithString:@"foo"];

alloc+init 创建一个保留对象,self.myString = 再次增加保留计数。如果编码正确,dealloc 方法会通过以下方式之一释放属性:

[myString 发布]; 或者 self.myString = nil;

这会处理使用 self.myString = 添加的保留,但不会处理创建时的保留。解决方案,以下之一:

myString = [[NSString alloc] initWithString: @"foo"]; // 不调用 setter 方法,因此不会保留赋值 - 但不调用 setter,如果非平凡的 setter 可能会很糟糕。

self.myString = [[[NSString alloc] initWithString:@"foo"] autorelease];

autorelease 释放 alloc+init 保留。

当然,这是一个人为的例子,因为你可能真的会使用:

self.myString = [NSString stringWithString: @"foo"];

这是一个类方法,返回一个自动释放的字符串并避免了这个问题。但我们的想法是展示一个简单的例子来解释这类问题。

还有许多其他方法无法正确释放内存,但建议您按照自己的方式备份调用链,直到找到您的代码,这是查看触发内存分配的位置的方法,然后然后你就可以弄清楚为什么你没有正确发布它。

【讨论】:

【参考方案4】:

尝试在你的代码中做一些问题: 1.请避免像

这样隐藏声明
NSString *string = [dictionary valueForKey:[dictionary2 valueForKey:@"something"]]

正确的代码是:

NSString *key = [dictionary2 valueForKey:@"something"];  
NSString *string = [dictionary valueForKey:key];
key = nil;

    尽量避免使用自动释放和本地声明,如:

    NSArray *array = [NSArray 数组];

如果你这样做,请确保你有:

NSArray *array = [NSArray array];
.... some code where array is using;
array = nil;

更好的想法是分配并在您不需要对象时立即释放并将其设置为零。 3.检查你是否使用了正确的设置器。可能更好的主意是避免使用它,根据我的经验,为之前使用的 getter 和 setter 释放类开始泄漏。

如果你发布了你的代码的某些部分,你看到了大多数泄漏(工具让你有可能点击泄漏的对象并查看编程代码中的泄漏量)社区可以提出更多建议。

【讨论】:

以上是关于由于 CoreFoundation 框架导致的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

UIKitCore、CoreFoundation 等缺少 dSYM

从0开始学Swift笔记整理

《从零開始学Swift》学习笔记(Day60)——Core Foundation框架

《从零开始学Swift》学习笔记(Day 62)——Core Foundation框架之内存托管对象与非托管对象

基于内存和 Redis 的两级 Java 缓存框架

node/小程序过大导致内存溢出(已解决)