【问题标题】:Why is calling subarrayWithRange multiple times in a loop causing a NSMallocException?为什么在循环中多次调用 subarrayWithRange 会导致 NSMallocException?
【发布时间】:2014-05-03 22:32:25
【问题描述】:

我有一个大的 NSArray (wordDictionary),我正在一个 for 循环中从它创建更小的子数组。如果 for 循环设置为 20,000 次迭代,一切正常。但是,如果我将 for 循环迭代次数增加到 200,000 次迭代,我会得到一个 malloc 错误……这是为什么呢?

我注意到如果我将子数组分配从循环内部移动到循环外部,它就解决了问题!(?)请注意,所有子数组在两种情况下都是相同的(这只是为了演示问题)。这是循环内的赋值代码(导致 malloc 错误):

    NSArray *subArray;
    //subArray = [wordDictionary subarrayWithRange:(NSRange){50000,20000}];
    for (int i=0;i<200000;i++)
    {
       subArray = [wordDictionary subarrayWithRange:(NSRange){50000,20000}];
       testBool = [subArray containsObject:@"hello"];
    }
    NSLog(@"Done");

如果将子数组赋值移到循环外(如注释行所示),则上述代码有效

在我收到的错误消息中包含以下内容:

* mach_vm_map(size=8388608) 失败(错误代码=3) 错误:无法分配区域 由于未捕获的异常“NSMallocException”而终止应用程序 原因:'* NSAllocateObject():尝试分配类'__NSArrayI'的对象失败' libc++abi.dylib:以 NSException 类型的未捕获异常终止

欢迎任何关于可能导致此问题以及如何解决此问题的提示!谢谢!!

【问题讨论】:

  • 内存不足了吗?大小为20000 的数组似乎很大。
  • wordDictionary (NSArray) 中有近 200,000 个元素。在这个例子中,subArray 有 20,000 个元素。但是只有一个实例,它被一遍又一遍地重复使用,所以我认为它应该没问题。但是,当分配在循环之外时,一切正常。因此,如果我对 subArray 的分配存在内存泄漏并且它被调用了 200K 次,那很可能是原因......
  • 因为循环中没有@autorelease 块。

标签: objective-c xcode


【解决方案1】:

我的猜测是你的内存不足。每次调用 -subarrayWithRange 时,您都会分配一些内存(取决于实现,但可能是 20000 * 一些)。

现代 Objective-C 使用自动引用计数而不是垃圾回收。这意味着内存不会立即释放,即使您将另一个对象分配给 subArray

尝试将其移动到本地自动释放池中:

for (int i=0;i<200000;i++)
{
    @autoreleasepool {
        // your code
    }
}

【讨论】:

  • 同意。实际上这种(奇怪的)语法也有效:for (...) @autoreleasepool { ... }.
  • @Droppy:这种语法没有什么奇怪的。 @autoreleasepool { ... } 是一个语句,这正是 for 语句的主体。
  • 嗯,我觉得它看起来很奇怪,不常用,所以我提到了它。
  • @autoreleasepool 常被不想耗尽内存的人使用。
【解决方案2】:

即使您遵循给出的建议并创建一个自动释放池,这也是非常低效的。您每次提取 20,000 个对象的范围,这意味着将保留并稍后释放 20,000 个对象,完全没有充分的理由。它很容易避免:

for (int i=0;i<200000;i++)
{
   NSRange range = NSMakeRange (50000,20000);
   testBool = [wordDictionary indexOfObject:@"hello" inRange:range] != NSNotFound;
}
NSLog(@"Done");

您可能还会考虑 NSSet 或 NSOrderedSet 是否不是正确的数据结构。

【讨论】:

  • 你们太棒了!实际上,我确实注意到在我的代码中使用这个清除内存的新调用对性能造成了大约 20% 的小幅影响。这不是那么多,但我明白你的意思。您在这种情况下根本不使用 subArray 的建议很棒。我将不得不消化它并运行一些测试,看看我可以用你的方式来加快速度。谢谢!
  • 仅供参考:在我的情况下,我有一个排序的 200K 单词的列表。然后我有一个包含 26 个索引的表,其中 a-z 字母开始。如果我在整个列表中搜索一个单词,每次搜索大约需要 3 毫秒。但是如果我使用我的索引表,我可以将时间缩短到 0.5 毫秒(使用 autorealesepool)。但是我仍然没有使用单词是有序的事实,所以我必须做更多的事情来进一步加快速度。不使用 subArray 和 autorelease 池将是我必须进一步探索的东西。我还将研究 NSOrderedSet 建议。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2013-10-17
  • 2013-04-30
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
相关资源
最近更新 更多