GCD-> blocks-> C ++ & # 8594; SQLCipher: Sqlite3MemMalloc memory is not released after task completes

I am working on a database application and am using SQLCipher to encrypt the database. I am using GCD sequential queue for all database operations and manual memory management. Below is the code snippet for my database related operations. I have more methods such as saveRecordData:

which calls executeOnGCD:

for different post types.

-(void)saveRecordData:(NSArray *)dataObjects{
    [self executeOnGCD:^{
        std::vector<RecordData> list;

        for(id object in dataObjects){
            RecordData recordDataObject(/*create c++ data object*/)
            list.push_back(recordDataObject);
        }

        DataBaseManager::GetInstance()->saveData(list);
    }];
}

-(void)executeOnGCD:(void (^)())block{
    __block UIBackgroundTaskIdentifier task = UIBackgroundTaskInvalid;

    task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:task];
        task=UIBackgroundTaskInvalid;
    }];

    void (^executionBlock)() = ^(){
        @autoreleasepool {
            block();
            [[UIApplication sharedApplication] endBackgroundTask:task];
            task=UIBackgroundTaskInvalid;
        }
    };
    //dataManagerQueue is serial queue.
    dispatch_async(self.dataManagerQueue, executionBlock);
}

      

Here DataBaseManager is a C ++ class that does the actual insert of the database. The DataBaseManager implementation of saveData is as follows.

void saveData(std::vector<RecordData> list) {
    for(RecordData record : list)
    {
        sqlite3_stmt * statement = nullptr;

        if(sqlite3_prepare_v2(m_dbConnection, INSERT_STATEMENT, -1, &statement, NULL) == SQLITE_OK)
        {
            //Bind parameters
            int index = sqlite3_bind_parameter_index(statement, COLUMN1);
            sqlite3_bind_int(statement, index, value1);

            index = sqlite3_bind_parameter_index(statement, COLUMN2);
            sqlite3_bind_int(statement, index, value2);

            /*Bind other parameters*/

            sqlite3_step(statement);
        }
        sqlite3_finalize(statement);
    }
}

      

When my app saves 80k + records using the saveData function, there is a difference in memory allocation between starting and finishing the operation. The tools assume that there is still memory allocated by SQLCipher that has not been released even after 5 minutes of completion. As shown in the picture below at the beginning of the operation, the memory allocation was 1.90 MB, and after the completion of the task memory, it decreased to 6.6mb to 1.90mb

enter image description here

Also, once the operation is complete, the memory decreases very slowly and takes about 6 minutes to reduce the memory size from 20 to 6.6 MB.

my questions

1) why didn't the memory drop to ~ 1.90mb after the task finished? Does this have to do with using C ++ objects in GCD?

2) Why does it take ~ 6 minutes to reduce the memory from 20 MB to 6.6 MB? Are you releasing GCD memory slowly?

3) I also see a block _dispatch_alloc_try_create_heap

allocated by libdispatch.dylb that is also not freed after the operation completes.

4) Am I using the correct approach for nested blocks in a method executeOnGCD:

?

+3


source to share


1 answer


I don't know the exact answer to the situation you have, but I have some suggestions and they were more than fit in the comment ...

What you want to do is look at the free event trail stacks that happen within 6 minutes of completion. This should give you some hints as to how the exemptions are planned. It seems likely that some background threads are doing cleanup. Retrieving these stack traces should allow you to search for code for where this cleanup was originally planned, and that should provide the answers you are looking for.



The close similarity between the up and down slopes of this graph leads me to think that (conceptually) the timer starts when the last object is used, and that there is some mechanism that reaps them after some period of inactivity. This is probably implemented by registering objects with some registry malloc time, then updating the field lastUsed

each time the object is used, and then the periodic process scans the list and effectively says "releases all objects that have not been used in X time". This can exist to provide some sort of implicit caching mechanism.

I don't know any of this for sure, but it seems like a likely explanation.

0


source







All Articles