【问题标题】:Cocoa App using much more Memory than it shouldCocoa App 使用的内存比它应该使用的多得多
【发布时间】:2015-04-19 07:56:20
【问题描述】:

我正在开发一个从 API 下载和解析大约 40k json 文件的应用程序。由于我的程序结构,我不会立即解析它们,而是将它们保存在 NSMutable 数组中。每个 json 大约 1KB,其中大多数甚至更少。如果我的计算是正确的,那应该会产生大约 40MB(+ 一些开销)的分配内存。但是当我运行该应用程序时,内存使用量攀升至超过 4GB。

我在这里泄露了什么吗?由于我使用垃圾收集,我不应该需要释放任何东西,对吧?还是我的计算完全错误?

这是我的代码:

- (void) loadItemsForIds:(NSArray*)idList {

    for (NSNumber* n in idList) {
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://someapi.com/somejson.json?id=%@", n]];
        NSData* response = [NSData dataWithContentsOfURL:url];
        NSDictionary *loadedData = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];

        @synchronized(self.updateData) {
            [self.updateData addObject:loadedData];
        }

        [self performSelectorOnMainThread:@selector(progressLoadingInterface:) withObject:[loadedData valueForKey:@"name"] waitUntilDone:NO];
    };

}

编辑:

发现问题甚至存在,如果我不将数据保存到数组中。经过一些研究,我偶然发现了这个处理相同问题的问题:iPhone - Memory Leak - NSData dataWithContentsOfUrl & UIWebView

那么我的问题可能需要删除。

【问题讨论】:

标签: objective-c json cocoa memory-leaks nsmutablearray


【解决方案1】:

[NSJSONSerialization JSONObjectWithData:response options:0 error:nil] 将 JSON 数据解析为对象。这将比 1K 的 JSON 数据占用更多的内存。

此外,NSString 可能会将您的所有字符串数据存储为 unichar,这是一种 16 位表示:是 JSON 数据中假定的 UTF-8 编码大小的两倍。

根据 JSON 的内容,我可以相信 1K 的 JSON 数据可以变成 10K 的对象。


但这都是猜测。使用 Instruments (Product > Profile) 检查内存使用情况。这将使您更好地了解哪些对象占用了所有内存。

【讨论】:

  • 使用 Instruments 进行分析时,一切似乎都很好,在 3000 个 json 对象之后的持久字节为 37MB,这很好。但是一旦我在不使用 Instruments 的情况下尝试它,内存使用量就会高得多(3000 个对象后大约 181MB)我不明白这里发生了什么
【解决方案2】:

您永远不会在 40k 次迭代之间释放内存。
=> 你也永远不会给任何人这样做的机会(arc/gc)

将循环 body 包裹在 @autoreleasepool {...} 中,这样会耗尽内存

for (NSNumber* n in idList) {
    @autoreleasepool {
         NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://someapi.com/somejson.json?id=%@", n]];
        NSData* response = [NSData dataWithContentsOfURL:url];
        NSDictionary *loadedData = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];

        @synchronized(self.updateData) {
            [self.updateData addObject:loadedData];
        }

        [self performSelectorOnMainThread:@selector(progressLoadingInterface:) withObject:[loadedData valueForKey:@"name"] waitUntilDone:NO];
    }
}

【讨论】:

  • 那没有成功.. 另外:我认为 ARC 会在结束时自动释放循环内分配的内存?
  • 是的,但是没有池,内存会一直上升,并且只有在所有迭代后才会释放
  • 那么你的对象确实那么大 :D 描述对象有多大
猜你喜欢
  • 1970-01-01
  • 2015-05-31
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
相关资源
最近更新 更多