我正在研究数据库应用程序并使用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 ++类。 DataBaseManager的saveData实现如下。
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);
}
}
当我的应用程序使用saveData函数保存80k +记录时,操作开始和操作结束之间存在内存分配差异。仪器表明仍有SQLCipher分配的内存,即使在5分钟的操作完成后也没有释放。如下图所示,在操作开始时,内存分配为1.90mb,完成后任务内存减少到6.6mb不到1.90mb
一旦操作完成,内存减少非常缓慢,大约需要6分钟,以便将内存从20mb减少到6.6mb。
我的问题是
1)为什么在完成任务后内存没有下降到~1.90mb?这是否与在GCD中使用c ++对象有关?
2)为什么需要大约6分钟才能将内存从20mb减少到6.6mb? GCD会慢慢释放内存吗?
3)我还看到libdispatch.dylb分配的_dispatch_alloc_try_create_heap
块,在操作完成后也没有释放。
4)我是否在executeOnGCD:
方法中使用了正确的嵌套块方法?
答案 0 :(得分:0)
我不知道你所处的情况的确切答案,但我有一些建议,而且它们不仅仅适用于评论......
这里你要做的是查看完成后6分钟内发生的免费事件的堆栈跟踪。这应该给你一些关于如何安排解除分配的线索。似乎有一些后台线程正在进行清理。获取这些堆栈跟踪应该允许您在代码中搜索最初安排清理的位置,并且应该提供您寻求的答案。
该图的向上和向下斜率之间的紧密相似性使得 me 认为(概念上)在最后一次使用该对象时启动了一个计时器,并且有一些机制可以在一些对象之后重新使用它们。不活动的时期。这可能是通过在malloc时间使用某个注册表注册对象来实现的,然后在每次使用对象时更新lastUsed
字段,然后定期进程遍历列表并有效地说“释放所有避开的对象”已被用于X时间。“这可能存在以提供某种隐式缓存机制。
我不确定这一点,但这似乎是一个可能的解释。