ios — GCD-> blocks-> C ++ -> SQLCipher: память Sqlite3MemMalloc не освобождается после завершения задачи

Я работаю над приложением базы данных и использую SQLCipher для шифрования базы данных. Я использую последовательную очередь GCD для всех операций с базой данных и ручного управления памятью. Ниже приведен фрагмент кода для моих операций, связанных с базой данных. У меня есть больше методов, таких как saveRecordData: который вызывает executeOnGCD: для разных типов записей.

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

Здесь DataBaseManager — это класс C ++, который выполняет фактическую вставку базы данных. Реализация saveData в DataBaseManager показана ниже.

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

Когда мое приложение сохраняет записи размером более 80 тыс. С использованием функции saveData, существует разница в распределении памяти между моментом начала и окончания операции. Инструменты предполагают, что SQLCipher все еще выделяет память, которая не освобождается даже после 5 минут завершения операции. Как показано на рисунке ниже, в начале операции выделение памяти составляло 1,90 МБ, а после завершения задачи объем памяти уменьшается до 6,6 МБ, а не до 1,90 МБ.

введите описание изображения здесь

Кроме того, после завершения операции память уменьшается очень медленно и занимает около 6 минут, чтобы уменьшить объем памяти с 20 МБ до 6,6 МБ.

мои вопросы

1) почему память не упала до ~ 1.90mb после завершения задачи? Это связано с использованием объектов C ++ в GCD?

2) Почему требуется ~ 6 минут, чтобы уменьшить объем памяти с 20 до 6,6 МБ? GCD освобождает память медленно?

3) я тоже вижу _dispatch_alloc_try_create_heap блок, выделенный libdispatch.dylb, который также не освобождается после завершения операции.

4) я использую правильный подход для вложенных блоков в executeOnGCD: метод?

3

Решение

Я не знаю точного ответа на вашу ситуацию, но у меня есть некоторые предложения, и они были более чем соответствуют комментарию …

То, что вы хотите сделать здесь, это посмотреть на следы стека бесплатных событий, которые происходят в течение 6 минут после завершения. Это должно дать вам некоторые подсказки относительно того, как планируются сделки. Кажется вероятным, что какой-то фоновый поток выполняет очистку. Получение этих трассировок стека должно позволить вам найти код, для которого первоначально запланирована эта очистка, и который должен предоставить ответы, которые вы ищете.

Близкое сходство между восходящими и нисходящими наклонами этого графика делает мне подумайте, что (концептуально) таймер запускается, когда объект используется в последний раз, и что существует некоторый механизм, который пожинает их после некоторого периода бездействия. Это, вероятно, реализуется путем регистрации объектов в некотором реестре во время malloc, а затем обновления lastUsed поле для каждого использования объекта, а затем периодический процесс обходит список и фактически говорит: «освободите все объекты, которые не использовались в течение X времени». Это может существовать, чтобы обеспечить некоторый неявный механизм кэширования.

Я не знаю ничего из этого наверняка, но это кажется вероятным объяснением.

0

Другие решения


По вопросам рекламы [email protected]