首页 > 解决方案 > 当我读取大小为 17 mb 的 100 万个 url 文件时,我的程序占用 163 mb

问题描述

我正在通过以下方式读取文件。

std::vector<std::string> urlList;
std::ifstream infile("top1m.txt");
std::string line;
std::cout << "Loading urls from file" << std::endl;
for (int offset = 0; offset < _urlCount; offset++) {  //_urlCount=1000000
    std::getline(infile, line);
    if (!line.empty()){
        if (line.back() == '\r')
            line.erase(line.length() - 1, std::string::npos);
    }
    urlList.push_back(std::string("http://").append(line));
}
inflie.close();

top1m.txt 为 17 mb。在阅读文件之前,我的 .exe 文件只有 6 mb。我在做什么错以及如何减少程序占用的内存?

标签: c++filememorymemory-managementmemory-leaks

解决方案


假设您在开始循环之前知道 _urlCount 值,那么节省内存的最简单方法是在开始循环之前保留向量的元素,使用以下行:

urlList.reserve(_urlCount);

这将节省大量内存的原因是因为任何向量的实现都是通过保留初始少量元素来工作的,通常是第一次变为非空时,然后每次 push_back 都会超过它分配的向量的存储容量一个更大的缓冲区,其容量是旧容量的某个常数倍(有时这个常数是 2,但在实现之间会有所不同),然后它将现有元素从旧缓冲区复制到新缓冲区,然后再添加新元素。旧的缓冲区经常留在内存中作为重用的候选者。

因此,假设为原始大小选择的容量为 8 个元素,增长因子为 2,并忽略 malloc 开销,但假设较小的缓冲区没有被重用,至少在您检查大小的时候没有程序。

当您有 1 个字符串时,第一个向量体可容纳 8 个字符串。当您有 8+1 个字符串时,新向量体的容量为 2*8,但您仍然拥有(作为释放的内存)旧向量体的旧缓冲区,容量为 8。

当您拥有 1,000,000 个字符串时,您可能在内存中拥有以下缓冲区:

1 个用于向量体的缓冲区,可容纳 2 ** 20 个字符串

1 个空闲缓冲区,可容纳 2 ** 19 个字符串

1 个空闲缓冲区,可容纳 2 ** 18 个字符串

...

1 个空闲缓冲区,可容纳 8 个字符串

当然,这些较小的缓冲区中的一些可能会在某个时候被重用,我只是弥补了 8 的初始容量并使用了 2 的增长因子,因为我在 std::vector 的一些旧实现中看到了它,我我也忽略了有关 std::string 实现的细节,但你明白了。

当您预先为 1,000,000 个字符串保留空间时,您将只获得一个用于向量体的大缓冲区。考虑到字符串的大小,您仍将拥有 1,000,000 个与 URL 字符串关联的较小缓冲区,并且您可以通过不使用 std::string 来节省大量空间,但这是另一回事,本答案中未解决。


推荐阅读