Christoph P.
Christoph P.

Reputation: 700

Realm: mmap() failed: Cannot allocate memory size

I'm using Realm 2.4.3 in a large iPad Application for one of our customer.
Most of the time Realm performed very well, but now the number of Records is increasing and a get some memory issues.

App

(unexpected) behaviour

Issues i already read

The fix I tried

the writeCopyToURL:encryptionKey:error: compact hack

I have a Singleton Object which handles all storage actions to realm, which has a writeTransaction: method which handles beginWriteTransaction and commitWriteTransaction handling. All storage actions are coming through this method.

- (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];
    }
}

The fix works in the storage!! The file shrinks from 2,5GB to 500KB. But i still got the issue that realm wants to allocate too much memory:

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}

Is there somebody with an idea to fix this ? :-)
If i missed some necessary information please leave a comment.. i'm in this issue for several days and my brain is like 💥

Upvotes: 6

Views: 4605

Answers (1)

edhnb
edhnb

Reputation: 2192

We had this error. Below is our workaround until Realm fixes the issue:

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;
    }
});

Upvotes: 2

Related Questions