核心数据executeFetchRequest消耗大量的内存
我在核心数据库中插入cca 100 000记录。数据库包含3个实体。
玩家,俱乐部,玩家俱乐部
实体在关系中:
玩家 PlayerClub 俱乐部
当插入PlayerClub时,我注意到了很多在插入大约50 000个记录之后的存储器消耗和速度损失。记录永远不会更新,因为PlayerClub只有唯一的值。
由于测试原因,我已经清空了Player和Club表并再次运行程序。没有速度下降,但内存消耗是巨大的。这是代码:
I am inserting cca 100 000 records in core data database. Database contains 3 entities. Player, Club, PlayerClub Entities are in relations: Player<-->>PlayerClub<<-->Club While inserting in PlayerClub I have noticed a lot of memory consumption and loss of speed after about 50 000 records have been inserted. Records are never updated cause PlayerClub has only unique values. For test reasons I have emptied Player and Club tables and run the program again. There was no speed drop but memory consumption was huge again. This is the code:
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:ap.persistentStoreCoordinator];
[context setUndoManager:nil];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"PlayerClub"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSEntityDescription *entityKlubovi = [NSEntityDescription entityForName:@"Club" inManagedObjectContext:context];
NSFetchRequest *requestKlubovi = [[NSFetchRequest alloc] init];
[requestKlubovi setEntity:entityKlubovi];
[requestKlubovi setFetchLimit:1];
NSEntityDescription *entityIgraci = [NSEntityDescription entityForName:@"Player" inManagedObjectContext:context];
NSFetchRequest *requestIgraci = [[NSFetchRequest alloc] init];
[requestIgraci setFetchLimit:1];
[requestIgraci setEntity:entityIgraci];
for (int i=0; i<[parsedKlubIgraci count]; i++) {
@autoreleasepool {
KlubIgrac *klubIgrac = [[KlubIgrac alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
klubIgrac.iD = p.id;
//... add some othe properties
variables = [NSDictionary dictionaryWithObject:p.igracID forKey:@"ID"];
localPredicate = [predicateIgraci predicateWithSubstitutionVariables:variables];
[requestIgraci setPredicate:localPredicate];
klubIgrac.igrac = [self DohvatiIgracZaIgracId:p.igracID cntx:context request:requestIgraci predicate:localPredicate];
variables = [NSDictionary dictionaryWithObject:p.klubID forKey:@"ID"];
localPredicate = [predicateKlubovi predicateWithSubstitutionVariables:variables];
[requestKlubovi setPredicate:localPredicate];
klubIgrac.klub = [self DohvatiKlubZaKlubId:p.klubID cntx:context request:requestKlubovi predicate:localPredicate];
}
}
+(Club *)DohvatiKlubZaKlubId:(int)klubid cntx:(NSManagedObjectContext *)context
request:(NSFetchRequest *)request predicate:(NSPredicate *)predicate
{
@autoreleasepool {
NSError *error;
NSArray *arTmp;
arTmp = [context executeFetchRequest:request error:&error];
Club *klub;
if(arTmp != nil && [arTmp count]){
return [arTmp objectAtIndex:0];
}
}
}
DohvatiIgracZaIgracId method基本上是和DohvatiKlubZaKlubId相同的方法,所以我dinnt后它。
这段代码调用大约100 000次。之前的内存消耗大约为150 MB。在它完成其650 MB之后。所以它消耗500 MB,不保存上下文,并且没有从数据库获取任何东西,因为表是空的。如果我注释
DohvatiIgracZaIgracId method is basically the same method as DohvatiKlubZaKlubId so I dinnt post it. This code is called about 100 000 times. The memory consumption before is about 150 MB. After it finishes its 650 MB. So it consumes 500 MB, without saving the context and without fetching anything from the database cause the tables are empty. If I comment
arTmp = [context executeFetchRequest:request error:&error];
行的内存消耗降至200 MB。所以差别是400MB的一行代码什么都不做。这不可能。任何人都有一些想法。过了一会儿我的应用程序消耗了超过2.5 GB。
line in DohvatiIgracZaIgracId and DohvatiKlubZaKlubId methods the memory consumption falls to 200 MB. So the diference is 400MB for a line of code that does nothing. This cant be. Anyone has some ideas. After a while my app consumes more than 2.5 GB.
这些信息是在模拟器上完成的,因为我需要建立一个数据库,我会在以后预先加载,所以速度是重要性:)...提前Thx
The mesuments were done on simulator cause I need to build a database that I will preload later, so the speed is of importance :)... Thx in advance
em>
without saving the context
这是经验丰富的Core Data开发人员说oh holy crap的问题的一部分。这是你最大的问题。定期保存更改 - 每50个条目,或每100个,但无论如何,不要等到您完成。您正在强制Core Data将内存中的所有对象保存为未保存的更改。这是您遇到记忆问题的最大原因。
This is the part of the question where experienced Core Data developers say "oh holy crap". That's your biggest problem right there. Save changes at regular intervals-- every 50 entries, or every 100, but whatever you do, don't wait until you're finished. You're forcing Core Data to keep all of those objects in memory as unsaved changes. This is the biggest reason you're having memory problems.
您应该考虑的其他一些事情:
Some other things you should consider:
-
不要一次抓取一件物品。获取费用相对较高。如果你运行100k的实例并且每次获取一个,你的代码将花费几乎所有的时间执行抓取。提取50-100的批次(您可以调整数字,以获得速度与内存使用的最佳平衡)。
Don't fetch your objects one at a time. Fetches are relatively expensive. If you run through 100k instances and fetch each of them one at a time, your code will be spending almost all of its time executing fetches. Fetch in batches of 50-100 (you can tune the number to get the best balance of speed vs. memory use). Process one batch, then save changes at the end of the batch.
当您完成获取的对象时,告诉托管对象上下文,重做。通过调用 refreshObject:mergeChanges:
和 NO
作为第二个参数来做到这一点。这告诉上下文它可以释放它用于对象的任何内部存储器。
When you're done with a fetched object, tell the managed object context that you're done. Do this by calling refreshObject:mergeChanges:
with NO
as the second argument. That tells the context it can free up any internal memory it's using for the object. This loses any unsaved changes on the objects, but if you haven't made any changes then there's nothing to lose.
请考虑清除 PlayerClub 实体。核心数据支持多对多关系。这种实体几乎从来没有用。您使用的是核心数据,而不是SQL,因此请勿像设计您的实体类型一样。
Consider getting rid of the PlayerClub
entity completely. Core Data supports many-to-many relationships. This kind of entity is almost never useful. You're using Core Data, not SQL, so don't design your entity types as if you were.