当大小很大时,QList崩溃

时间:2017-04-10 15:36:25

标签: qt qlist qsqlquery

我正在使用QList来存储从SQL表中读取的数据。该表有超过一百万条记录。我需要将它们放在列表中,然后在列表上进行一些处理。

QList<QVariantMap> list;

QString selectNewDB = QString("SELECT * FROM newDatabase.M106SRData");
QSqlQuery selectNewDBQuery = QSqlDatabase::database("CurrentDBConn").exec(selectNewDB);
while (selectNewDBQuery.next())
{
    QSqlRecord selectRec = selectNewDBQuery.record();
    QVariantMap varMap;
    QString key;
    QVariant value;
    for (int i=0; i < selectRec.count(); ++i)
    {
        key = selectRec.fieldName(i);
        value = selectRec.value(i);
        varMap.insert(key, value);
    }
    list << varMap;
}

我得到“qvector.h,第534行:内存不足”错误。

当列表达到&lt; 1197762项&gt;的大小时,程序崩溃。我尝试使用reserve()但它没有用。 QList是否限制在特定大小?

2 个答案:

答案 0 :(得分:3)

由于C ++运行时报告它无法分配更多内存,因此内存不足。这对Qt容器来说不是问题。由于索引的使用int的大小,容器限制为2 ^ 31-1项。你不在那附近。

至少:

  1. 使用QVector代替QList,因为QVariantMap元素的开销要低得多。

  2. 如果查询允许,尝试保留空间:这几乎会使内存要求减半!

  3. 如果可以的话,编译64位目标。

  4. QVector<QVariantMap> list;
    
    QString selectNewDB = QString("SELECT * FROM newDatabase.M106SRData");
    QSqlQuery selectNewDBQuery = QSqlDatabase::database("CurrentDBConn").exec(selectNewDB);
    auto const size = selectNewDBQuery.size();
    if (size > 0) list.reserve(size);
    while (selectNewDBQuery.next())
    {
        auto selectRec = selectNewDBQuery.record();
        QVariantMap varMap;
        for (int i=0; i < selectRec.count(); ++i)
        {
            auto const key = selectRec.fieldName(i);
            auto const value = selectRec.value(i);
            varMap.insert(key, value);
        }
        list.append(varMap);
    }
    

答案 1 :(得分:0)

你要么没有足够的内存,要么更有可能使用32位Qt版本,它不能使用超过4 GB的内存。或者两者都有。容量本身应该能够处理超过20亿个元素。

QList也没有帮助,因为在您的情况下,它可能会将每个元素存储为指针,并为实际变体映射执行额外的堆分配。因此,最终会产生相当大的额外堆分配开销。

由于查询已包含大量数据,因此它本身可能会占用相当多的内存。

除非你已经禁用了页面文件,否则自己用完ram不应该导致崩溃,因为它只会启动分页并破坏性能,但会继续运行,所以你可能会遇到32位进程的内存限制,可能只有2 GB。

除了做Kuba在他的回答中建议的事情之外,您可能希望将查询分成更小的部分,并在可能的情况下将结果分成几个查询而不是一个,并一次处理一个,减少内存查询结果使用它,并在完成查询后释放内存以进行查询。

如果您有很多重复的字符串,还可以选择从QString保存RAM。由于它是隐式共享的,因此您可以拥有一堆完全使用相同基础数据的相同字符串。您可以利用此功能,使用QSet保留一组唯一字符串,并快速检查字符串是否已存在。然后,不使用查询结果中的字符串,而是使用集合中的字符串。通过集合中的值复制的所有相同字符串将重用相同的字符串数据。相比之下,您当前的方法将为每n个重复的字符串使用n个空间。