核心导入大型数据集时的数据内存使用情况

我现在用一个令人讨厌的核心数据问题困住了大约两个星期。 我阅读了大量的博文,文章和问题/答案,但仍然无法解决我的问题。

我跑了很多testing,并能够将较大的问题减less到一个较小的问题。 这将是一个很大的解释,所以跟我保持!

问题 – 数据模型

我必须得到以下datamodel

对象A与对象B有一对多的关系,与对象C有另一个一对多的关系。由于核心数据的build议,我必须创build反向关系,所以B的每个实例指向它的父对象A, C指向其父B.

A <->> B <->> C 

问题 – MOC设置

为了保持响应顺畅,我创build了一个三级的managedObjectContext结构。

  1. 父MOC – 使用NSPrivateQueueConcurrencyType在其自己的专用线程上运行,对于persistentStoreCoordinator
  2. MainQueue MOC – 使用NSMainQueueConcurrencyType在mainThread上运行,并具有父MOC 1
  3. 对于每个parsing操作,我创build了第三个MOC,它也有它的私有队列,并具有父mainQueue MOC

我的主数据控制器作为观察者被添加到MOC 2的NSManagedObjectContextDidSave通知中,所以每当MOC 2保存一个performBlock:在MOC1被触发,执行一个保存操作(由于performBlock:asynchronous)。

问题 – parsing

为了将一个大的JSON文件parsing到我的核心数据结构中,我写了一个循环parsing器。 这个parsing器首先创build一个新的MOC(3)。 然后它获取对象A的数据并parsing其属性。 然后parsing器读出B的JSON关系,并创build填充数据的相应对象。 这些新的对象通过调用addBObject: A添加到A.因为parsing器是经常性的,所以parsingB意味着parsingC,这里也创build新的对象并将其附加到B.这一切都发生在MOC 3的performBlock:上。

  • parsing(创build'A'对象并开始parsingB)
    • parsingA(创build'B'对象,将它们附加到A并开始parsingC)
      • parsingB(创build'C'对象,将它们附加到B)
        • parsingC(仅将数据存储在C对象中)

在每个parsing操作之后,我保存MOC 3,并在mainThread上调度主MOC(2)的保存操作。 由于NSManagedObjectContextDidSave通知,MOC 1将自动保存为asynchronous。

  if (parsed){ NSError *error = nil; if (![managedObjectContext save:&error]) NSLog(@"Error while saving parsed data: %@", error); }else{ // something went wrong, discard changes [managedObjectContext reset]; } dispatch_async(dispatch_get_main_queue(), ^{ // save mainQueueManagedObjectContext [[HWOverallDataController sharedOverallDataController] saveMainThreadManagedObjectContext]; }); 

释放我的内存足迹,因为我现在不需要分析数据我正在执行:

 [a.managedObjectContext refreshObject:a mergeChanges:NO]; 

为每个AIparsing。

因为我需要parsing大约10个具有大约10个C的10个B,所以产生了许多个ManageObject。

问题 – 仪器

一切工作正常。 唯一的是:当我打开分配工具时,我看到未发布的A,B和C的。 我从他们的保留金额或其他方面没有得到任何有用的信息。 而且由于我的实际问题涉及更复杂的数据模型,生物体成为严重的记忆问题。 有人能弄清楚我做错了什么吗? 使用正确的managedObject在其他managedObjectContexts上调用refreshObjects也不起作用。 只有一个硬reset似乎工作,但后来我放弃了用户界面使用的活物的指针。

我试过的其他解决scheme

  • 我试图创build单向关系而不是双向关系。 这造成了许多其他问题,导致核心数据不一致和怪异的行为(如悬挂对象和核心数据产生1-n关系而不是nn关系(因为反函数未知)。

  • 我试图刷新每个更改或插入的对象,当我检索任何对象的NSManagedObjectContextDidSave通知

这两个“解决scheme”(这是不行的),似乎也有点冒失。 这不应该是要走的路。 应该有一种方法可以在不增加内存占用的情况下保持UI平滑运行?

– CodeDemo

– 进一步的调查

在mainContext(mainSave之后)刷新每个使用的对象之后(这是繁琐的工作),对象的大小减less到48个字节。 这表示这些对象都是故障的,但是仍然有一个指针留在了内存中。 当我们有大约40.000个全部出错的对象时,仍然有1.920MB的内存,这个内存永远不会被释放,直到persistentManagedObjectContext被重置。 这是我们不想做的事情,因为我们放弃了对任何managedObject的引用。

罗宾,

我有一个类似的问题,我解决了不同的问题。 在你的情况下,你有第三个IMO,冗余MOC,母公司MOC。 就我而言,我让这两个MOC通过DidSave通知,通过持久的商店协调员以旧派的方式进行交stream。 新的面向块的API使这更简单和强大。 这让我重置孩子MOCs。 虽然从第三个MOC中获得了性能优势,但与我使用的SQLite行高速caching相比,它并不是那么有优势。 你的path消耗更多的内存。 最后,我可以通过跟踪DidSave通知,在创build项目时对其进行修剪。

顺便说一下,你也可能因为你的MALLOC_TINYMALLOC_SMALL VM区域的大小而大幅度增加。 我的尾随修整algorithm可以让分配器更快地重用空间,从而阻止这些有问题的区域的增长。 根据我的经验,这些地区由于它们庞大的常驻内存空间是我应用程序的主要原因,因此Retweever被杀害。 我怀疑你的应用程序遭受同样的命运。

当内存警告来了,我打电话给下面的代码片段:

 [self.backgroundMOC performBlock: ^{ [self.backgroundMOC reset]; }]; [self.moc save]; [self.moc.registeredObjects trimObjects]; 

-[NSArray(DDGArray) trimObjects]只是通过一个数组并刷新对象,从而修剪它们。

总之,核心数据似乎在许多MOC中出现的项目上实现了写入algorithm的复制。 因此,你有以意想不到的方式保留的东西。 我专注于在导入后打破这些连接,以最大限度地减less我的内存占用。 由于SQLite行高速caching,我的系统似乎可以正常运行。

安德鲁

对于每个NSManagedObjectContext ,为了某个特定的目的,你将要积累NSManagedObject实例

一个NSManagedObjectContext只是一个便条纸,你可以随意实例化并保存,如果你想保持在NSPersistentStore变化,然后丢弃。

对于parsing操作(第3层),尝试为操作创build一个MOC,执行parsing,保存MOC,然后丢弃它。

感觉就像你有至less一层MOC被强烈的引用太多。

基本上问每个MOC的问题。 “为什么要保持这个对象及其相关的孩子活着”。

我有一个非常相似的导入助手。

看看下面的代码,看看它是否可以帮助你

 __block NSUInteger i = 0; NSArray *jsonArray = ... for (NSDictionary *dataStucture in jsonArray) { [managedObjectContext performBlock:^{ @autoreleasepool { i++; A *a = (A*)[self newManagedObjectOfType:@"A" inManagedObjectContext:managedObjectContext]; [self parseData:[dataStucture objectForKey:@"a"] intoObject:a inManagedObjectContext:managedObjectContext]; [managedObjectContext refreshObject:a mergeChanges:YES]; if (i > 20) // Arbitrary number here { NSError *error = nil; [managedObjectContext save:&error]; [managedObjectContext reset]; } [managedObjectContext refreshObject:a mergeChanges:YES]; } dispatch_async(dispatch_get_main_queue(), ^{ [self saveMainThreadManagedObjectContext]; NSLog(@"DONE"); // parsing is done, now you see that there are still // A's, B's and C's left in memory. // Every managedObjectContext is saved and no references are kept // to any A, B and C so they should be released. This is not true, // so a managedObjectContext is keeping a strong reference to these // objects. }); }]; }