我正在为我们的一位客户在大型 iPad 应用程序中使用 Realm 2.4.3
。
大多数时候 Realm
表现得很好,但现在 Records 的数量在增加并且出现一些内存问题。
应用
- 部署目标:10.0
- BaseSDK:10.2
- 仅限 iPad
- Realm 2.4.3
- 该应用程序专为离线工作而设计,这就是我们使用 Realm 进行本地存储的原因
- 有一些方法可以通过 HTTP 将数据从后端同步到 Realm 数据库,反之亦然。
- Realm 数据库 中的记录数
(意外)行为
- realm 文件大小增长超过 2.5GB(压缩修复低于 ~500KB)
- realm 尝试将整个文件加载到内存中
- iPad Air 2 (2GB) 崩溃,在 iPad Pro (4GB) 上一切正常
我已经读过的问题
- mmap() 失败:无法分配内存
https://github.com/realm/realm-cocoa/issues/1159 - mmap() 失败:无法分配内存大小
https://github.com/realm/realm-cocoa/issues/3226 - 'RLMException',原因:'mmap() 失败:无法分配内存大小:1207959552'
https://github.com/realm/realm-cocoa/issues/3920
我尝试过的修复
writeCopyToURL:encryptionKey:error:
紧凑黑客
我有一个单例对象,它处理所有存储到 Realm 的操作,
它有一个 writeTransaction:
方法来处理 beginWriteTransaction
和 commitWriteTransaction
处理。所有存储操作都通过此方法进行。
- (void)writeTransaction:(void (^)(void))block
{
[self _ensureRealmThread:^{
NSDate *startDate = [NSDate date];
[[self _defaultRealm] beginWriteTransaction];
block();
NSError *commitWriteTransactionError = nil;
[[self _defaultRealm] commitWriteTransaction:&commitWriteTransactionError];
if (commitWriteTransactionError) {
NSLog(@"commit error: %@", commitWriteTransactionError);
}
NSTimeInterval time = [[NSDate date] timeIntervalSinceDate:startDate];
if (time > 0.5) {
NSLog(@"WARNING: Transaction duration > 0.5");
}
// i added these 5 lines to compact the database every 2000 requests
_writeTransactionIndex++;
if (_writeTransactionIndex > 2000) {
_writeTransactionIndex = 0;
[self compactDatabase];
}
}];
}
- (void)compactDatabase
{
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentsPath = searchPaths[0];
NSString *defaultCompactPath = [documentsPath stringByAppendingPathComponent:@"defaultCompact.realm"];
NSURL *defaultCompactURL = [NSURL fileURLWithPath:defaultCompactPath];
// remove old
if ([[NSFileManager defaultManager] fileExistsAtPath:[defaultCompactURL path]]) {
[[NSFileManager defaultManager] removeItemAtURL:defaultCompactURL
error:nil];
}
NSError *writeError = nil;
[[self _defaultRealm] writeCopyToURL:defaultCompactURL
encryptionKey:nil
error:&writeError];
if (!writeError) {
[[NSFileManager defaultManager] replaceItemAtURL:[self _defaultRealm].configuration.fileURL
withItemAtURL:defaultCompactURL
backupItemName:nil
options:NSFileManagerItemReplacementUsingNewMetadataOnly
resultingItemURL:nil
error:nil];
}
}
修复在存储中有效!!该文件从 2.5GB 缩小到 500KB。 但是我仍然遇到 Realm 想要分配太多内存的问题:
commit error: Error Domain=io.realm Code=9 "mmap() failed: Cannot allocate memory size: 268435456 offset: 2952790016" UserInfo={NSLocalizedDescription=mmap() failed: Cannot allocate memory size: 268435456 offset: 2952790016, Error Code=9}
有人有办法解决这个问题吗? :-)
如果我错过了一些必要的信息,请发表评论。我在这个问题上已经好几天了,我的大脑就像💥
最佳答案
我们遇到了这个错误。在 Realm 解决问题之前,以下是我们的解决方法:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_SERIAL, 0), ^{
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = [schema intValue];
config.shouldCompactOnLaunch = ^BOOL(NSUInteger totalBytes, NSUInteger usedBytes){
// totalBytes refers to the size of the file on disk in bytes (data + free space)
// usedBytes refers to the number of bytes used by data in the file
// Compact if the file is over 50MB in size and less than 50% 'used'
NSUInteger oneHundredMB = 50 * 1024 * 1024;
return (totalBytes > oneHundredMB) && (usedBytes / totalBytes) < 0.5;
};
__block BOOL deleteRealm = false;
//schema has changed. Set delete flag.
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
if (oldSchemaVersion < [schema intValue]) {
deleteRealm = TRUE;
}
};
[RLMRealmConfiguration setDefaultConfiguration:config];
//*** Memory Allocation bug 'fix/hack' ***
// Put realm init in try block. If it fails, blow it away in catch block. Then the following realm init will work.
// This fix eliminated the error for us, with no effect on perfomance.
@try
{
[RLMRealm defaultRealm];
}
@catch(...) {
NSURL *rurl = [RLMRealmConfiguration defaultConfiguration].fileURL;
// blow the database clean
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtURL:rurl error:&error];
if(error) {
NSLog(@"error %@ removing realm db", error);
} else {
NSLog(@"removed realm db successfully!");
}
}
[RLMRealm defaultRealm];
if (deleteRealm){
[[RLMRealm defaultRealm] beginWriteTransaction];
[[RLMRealm defaultRealm] deleteAllObjects];
[[RLMRealm defaultRealm] commitWriteTransaction];
deleteRealm = FALSE;
}
});
关于ios - Realm :mmap() 失败:无法分配内存大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42527836/