领域:mmap()失败:无法分配内存大小

时间:2017-03-01 09:23:04

标签: ios objective-c ipad realm mmap

我在一个大型iPad应用程序中为我们的一个客户使用Realm 2.4.3 大多数时候Realm表现得非常好,但现在记录的数量正在增加并且会产生一些内存问题。

应用

  • 部署目标:10.0
  • BaseSDK:10.2
  • 仅限iPad
  • Realm 2.4.3
  • 该应用程序旨在脱机工作,这就是我们使用Realm进行本地存储的原因
  • 有一些方法可以将数据从后端通过HTTP同步到Realm数据库,反之亦然。
  • 领域数据库中的记录数Number of Records

(意外)行为

  • 领域文件大小增长超过2,5GB(从低于500KB的紧凑修复)
  • realm尝试将整个文件加载到内存中
  • iPad Air 2(2GB)崩溃,在iPad Pro(4GB)上一切正常

我已阅读的问题

我尝试过的修复

writeCopyToURL:encryptionKey:error:紧凑型黑客

我有一个Singleton对象可以处理所有存储操作, 它具有处理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。 但我仍然遇到领域想要分配太多内存的问题:

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}

有人有想法解决这个问题吗? :-)
如果我错过了一些必要的信息,请发表评论..我在这个问题上好几天,我的大脑就像

1 个答案:

答案 0 :(得分:1)

我们遇到此错误。在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;
    }
});