核心导入大型数据集时的数据内存使用情况
我现在用一个令人讨厌的核心数据问题困住了大约两个星期。 我阅读了大量的博文,文章和问题/答案,但仍然无法解决我的问题。
我跑了很多testing,并能够将较大的问题减less到一个较小的问题。 这将是一个很大的解释,所以跟我保持!
问题 – 数据模型
我必须得到以下datamodel :
对象A与对象B有一对多的关系,与对象C有另一个一对多的关系。由于核心数据的build议,我必须创build反向关系,所以B的每个实例指向它的父对象A, C指向其父B.
A <->> B <->> C
问题 – MOC设置
为了保持响应顺畅,我创build了一个三级的managedObjectContext结构。
- 父MOC – 使用
NSPrivateQueueConcurrencyType
在其自己的专用线程上运行,对于persistentStoreCoordinator
- MainQueue MOC – 使用
NSMainQueueConcurrencyType
在mainThread上运行,并具有父MOC 1 - 对于每个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对象中)
- parsingB(创build'C'对象,将它们附加到B)
- parsingA(创build'B'对象,将它们附加到A并开始parsingC)
在每个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_TINY
和MALLOC_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. }); }]; }