2013-08-30 36 views
12

我的问题是关于核心数据和内存未被释放。我正在做一个同步过程从一个返回json的WebService导入数据。我在内存中加载要导入的数据,循环并创建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. 苹果文档

  2. 点的好重述导入时心里有数据到核心数据(Stackoverflow

  3. 测试完成并分析内存释放。他似乎和我有同样的问题,并且他发送了一份Apple Bug报告,但苹果没有回应。 (Source

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

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

    “我可以在内存稳定3MB进口数百万条记录,而不调用 -reset。”

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

测试:

数据样本:在创建总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 )
  • 设置上下文到整个过程后为零
  • 在不保存上下文的情况下执行整个过程(丢失信息)。实际上,结果是维持较少的内存量,使其保持在20 MB。但它仍然不会降低,...我需要存储的信息:)

也许我失去了一些东西,但我确实测试了很多,并按照指引后,我希望看到的内存减少再次。我运行Allocations工具来检查堆增长,这似乎也很好。也没有内存泄漏。

我运行的想法,以测试/调整......我真的很感激,如果有人能帮助我的还有什么我可以测试,也许指向我在做什么错误思想。或者它就是这样,它应该如何工作......我怀疑......

感谢您的任何帮助。

EDIT

我用仪器来确定与所述活动监视器模板和在“真实内存使用”中所示的结果的存储器使用量是相同获取打印在控制台与get_free_memory所述一个而内存仍然从未被释放。

+0

分析仪器中的内存使用而不是使用该功能会好得多。除了在不同时间显示内存使用外,它还会将内存分配与特定的代码行相关联。 –

+0

另外,它看起来每次通过内部循环都会处理相同的记录,因为'i'在该循环期间不会改变。 –

+0

嗨汤姆, 谢谢你的回应。我已经用Allocations模板描述了应用程序的堆增长和泄漏模板,一切似乎都很好......你会建议使用内存监视器吗?我对这个分析器不是很有经验。 实际上,'i'在第二个循环中递增了一个(您需要滚动才能看到它,代码并不完全适合框架)。这样做,我可以在'@ autoreleasepool'块结束之前保存批次,这是我在Research 5中读到的建议。 –

回答

10

确定这是相当尴尬...僵尸都在计划启动,对他们关闭的论点,但对诊断“启用僵尸对象”被选中......

关闭这个选项保持稳定内存。

感谢您阅读槽问题并试图解决它!

+0

令人尴尬...但是你让我从另外几个小时的沮丧中解脱出来......我完全忘记了我已经启用了它们,并且试图弄清楚为什么我的应用程序正在囤积内存! – RyanG

+0

同样在这里感谢分享只是浪费了我90分钟的生命。 – Gapp

+0

谢谢你从另一个尴尬的开发者:/ – Rog

2

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

我认为这里最重要的是这个配料也必须适用于@autoreleasepool

每迭代1000次就不足以排空自动释放池。您需要实际保存MOC,然后排空游泳池。

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

我在原版iPad 1上做了超过500,000条记录的测试。单独的JSON字符串的大小接近40MB。不过,这一切都没有崩溃,一些调整甚至导致可接受的速度。在我的测试中,我可以声称应用程序。原始iPad上有70MB的内存。

+0

嗨Mundi,谢谢你的回应。实际上你可以用这么多的记录进行测试是一种解脱。我希望我也能做到这一点。如果我把第二个'@ autoreleasepool'放到第二个for循环中,那么在这种情况下,在保存池之后会不会发生保存?实际上,我已经完成了两个for循环的设置,以便在您提到的引用之后排空池后进行保存......不会这个'@ autoreleasepool'影响保存吗? –

+0

只要你在* @ autoreleasepool中保存*,你应该有一个内存改进。请注意,在您的代码中,您可以在一个'@ autoreleasepool'中保存多次。 – Mundi

+0

如果我没有错,我只在每个'@ autoreleasepool'中保存一次。请注意,inner for循环是批处理(它也会增加外循环的索引“i”),上下文保存,池流失,并在​​外部的下一个循环中创建新的@ autoreleasepool,并创建下一个循环批处理将被处理 –