核心数据导入 - 不释放内存

时间:2013-08-30 16:16:07

标签: ios objective-c core-data memory memory-management

我的问题是核心数据和内存未发布。我正在进行同步过程,从WebService导入数据,返回一个json。我在内存中加载要导入的数据,循环并创建NSManagedObjects。导入的数据需要创建与其他对象有关系的对象,总共大约有11.000。但为了隔离问题,我现在只创建第一级和第二级的项目,将关系排除,这些是9043个对象。

我开始检查使用的内存量,因为应用程序在进程结束时崩溃(使用完整的数据集)。第一次内存检查是在内存中加载json之后,因此测量确实只考虑创建,并将对象插入到Core Data中。我用来检查使用的内存的是这段代码(source

-(void) get_free_memory {

    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), 
                                   TASK_BASIC_INFO,
                                   (task_info_t)&info,
                                   &size);
    if( kerr == KERN_SUCCESS ) {
        NSLog(@"Memory in use (in bytes): %f",(float)(info.resident_size/1024.0)/1024.0 );
    } else {
        NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
    }
}

我的设置:

  • 1持久商店协调员
  • 1主ManagedObjectContext(MMC)(NSMainQueueConcurrencyType用于读取(仅读取)应用程序中的数据)
  • 1后台ManagedObjectContext(BMC)(NSPrivateQueueConcurrencyType,undoManager设置为nil,用于导入数据)

BMC独立于MMC,因此BMC不是MMC的子环境。并且他们不共享任何父母背景。我不需要BMC通知MMC的更改。所以BMC只需要创建/更新/删除数据。

者平台:

  • iPad 2和3
  • iOS,我已经测试过将部署目标设置为5.1和6.1。没有区别
  • XCode 4.6.2
  • ARC

问题: 导入数据后,使用的内存不会停止增加,即使在进程结束后iOS似乎也无法耗尽内存。如果数据样本增加,则会导致内存警告以及应用程序关闭后。

研究

  1. Apple文档

  2. 将数据导入核心数据(Stackoverflow)时要记住的要点的详细概述

  3. 完成测试并分析内存释放。他似乎和我有同样的问题,他发送了Apple Bug报告但尚未得到Apple的回复。 (Source

  4. 导入和显示大型数据集(Source

  5. 表示导入大量数据的最佳方式。虽然他提到:

      

    “我可以在没有的情况下在稳定的3MB内存中导入数百万条记录   叫--reset。“

    这让我觉得这可能在某种程度上可能吗? (Source

  6. 试验:

    数据样本:共创建9043个对象。

    • 关闭了关系的创建,因为文档说他们“很贵”
    • 没有抓取

    代码:

    
    - (void)processItems {
        [self.context performBlock:^{
            for (int i=0; i < [self.downloadedRecords count];) {
                @autoreleasepool
                {
                    [self get_free_memory]; // prints current memory used
                    for (NSUInteger j = 0; j < batchSize && i < [self.downloadedRecords count]; j++, i++)
                    {
                        NSDictionary *record = [self.downloadedRecords objectAtIndex:i];
    
                        Item *item=[self createItem];
                        objectsCount++;
    
                        // fills in the item object with data from the record, no relationship creation is happening
                        [self updateItem:item WithRecord:record];
    
                        // creates the subitems, fills them in with data from record, relationship creation is turned off
                        [self processSubitemsWithItem:item AndRecord:record]; 
                    }
                    // Context save is done before draining the autoreleasepool, as specified in research 5)
                    [self.context save:nil];
    
                    // Faulting all the created items
                    for (NSManagedObject *object in [self.context registeredObjects]) {
                        [self.context refreshObject:object mergeChanges:NO];
                    }
                    // Double tap the previous action by reseting the context
                    [self.context reset];
                }
            }
        }];
        [self check_memory];// performs a repeated selector to [self get_free_memory] to view the memory after the sync 
    }
    

    Measurment:

    它从16.97 MB变为30 MB,同步后它降至28 MB。每5秒重复一次get_memory调用,将内存保持在28 MB。

    其他没有运气的测试:

    • 重新创建持久存储,如研究2)所示
    • 测试让线程等待一段时间来查看内存是否恢复,例如4)
    • 在整个过程之后将上下文设置为nil
    • 在不保存任何环境的情况下完成整个过程(为此信息丢失)。这实际上提供了维护较少内存量的结果,使其保持在20 MB。但它仍然没有减少......我需要存储的信息:)

    也许我错过了一些东西,但我确实经过了很多测试,按照指导方针后,我希望看到内存再次减少。我已经运行了Allocations工具来检查堆增长,这似乎也很好。也没有记忆泄漏。

    我的测试/调整的想法已经不多了......如果有人能帮助我了解我可以测试的其他内容,或者可能指出我做错了什么,我真的很感激。或者就是这样,它应该如何工作......我怀疑......

    感谢您的帮助。

    修改

    我使用工具来分析活动监视器模板的内存使用情况,“实际内存使用情况”中显示的结果与使用get_free_memory在控制台中打印的结果相同,内存仍然存在似乎永远不会被释放。

2 个答案:

答案 0 :(得分:10)

好的,这是非常令人尴尬的...僵尸已在该计划上启用,在参数上他们被关闭但在诊断“启用僵尸对象”被检查...

关闭此功能可使内存保持稳定。

感谢那些通过问题阅读并试图解决它的人!

答案 1 :(得分:2)

在我看来,关键的一点是你最喜欢的来源(“3MB,数百万条记录”)是提到的批处理 - 除了禁用撤销管理器,这也是推荐的Apple非常重要)。

我认为重要的是这个批处理也必须适用于@autoreleasepool

  

每1000次排出自动释放池是不够的   迭代。你需要实际保存MOC,然后耗尽池。

在您的代码中,尝试将第二个@autoreleasepool放入第二个for循环中。然后调整批量大小以进行微调。

我在原始iPad上进行了超过500.000条记录的测试1.仅JSON字符串的大小接近40MB。尽管如此,这一切都可以在没有崩溃的情况下运行,并且一些调整甚至可以达到可接受在我的测试中,我可以声称应用程序。原始iPad上有70MB内存。