使用Cocoa垃圾收集泄漏内存

时间:2010-01-19 17:00:10

标签: cocoa garbage-collection

我一直在撞墙,试图弄清楚我在垃圾收集的Cocoa应用程序中是如何发生内存泄漏的。 (活动监视器中的内存使用量会增长和增长,使用GC监视器工具运行应用程序也会显示不断增长的图形。)

我最终将其缩小为我的代码中的单个模式。数据被加载到NSData中,然后由C库解析(数据的字节和长度被传递到它中)。 C库具有回调函数,它将触发并返回子字符串的起始指针和长度(以避免内部复制)。但是,出于我的目的,我需要将它们转换为NSStrings并保持一段时间。我是通过使用NSString的initWithBytes:length:encoding:方法完成的。我假设会复制字节,而NSString会适当地管理它,但是出了点问题,因为这会像疯了一样泄漏。

此代码将“泄漏”或以某种方式欺骗垃圾收集器:

- (void)meh
{
    NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]];
    const int substrLength = 80;

    for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
        NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
        [cocoaString length];
    }
}

我可以将它放在计时器中,只需观察活动监视器以及GC监视器仪器的内存使用情况。 (holmes.txt是594KB)

这不是世界上最好的代码,但它显示了问题。 (我正在运行10.6,该项目的目标是10.5 - 如果这很重要)。我阅读了垃圾收集文档并注意到了一些可能的陷阱,但我认为我没有做任何明显违反规则的事情。不过要问,不要伤心。谢谢!

Project zip

这是对象图的增长和增长的图片:

alt text

1 个答案:

答案 0 :(得分:13)

这是一个不幸的边缘案例。请提交一个错误(http://bugreport.apple.com/)并附上您出色的最佳示例。

问题有两个;

  • 主事件循环未运行,因此,不通过MEL活动触发收集器。这使收集器执行其正常的仅基于阈值的集合。

  • 数据将从文件读取的数据存储到从malloc区域分配的malloc缓冲区中。因此,GC会计分配 - NSData对象本身 - 非常小,但指向非常大的东西(malloc分配)。最终结果是收集器的阈值没有被击中而且它没有被收集。显然,希望改善这种行为,但这是一个难题。

这是一个非常容易在微基准或孤立中重现的错误。在实践中,通常会有足够的事情发生,这个问题不会发生。但是,在某些情况下可能会出现问题。

将您的代码更改为此,收集器将收集数据对象。请注意,您不应经常使用collectExhaustively - 它会占用CPU。

- (void)meh
{
    NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]];
    const int substrLength = 80;

    for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
        NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
        [cocoaString length];
    }
    [data self];
    [[NSGarbageCollector defaultCollector] collectExhaustively];
}

[data self]使数据对象在最后一次引用后保持活动状态。