巨大的std :: vector <std :: vector>不会在销毁</std :: vector>时释放所有内存

时间:2014-09-24 15:16:28

标签: c++ memory vector

当使用非常大的矢量矢量时,我们发现部分内存未被释放。

#include <iostream>
#include <vector>
#include <unistd.h>

void foo()
{
    std::vector<std::vector<unsigned int> > voxelToPixel;
    unsigned int numElem = 1<<27;
    voxelToPixel.resize( numElem );

    for (unsigned int idx=0; idx < numElem; idx++)
        voxelToPixel.at(idx).push_back(idx);

}

int main()
{
    foo();
    std::cout << "End" << std::endl;
    sleep(30);
    return 0;
}

这会留下大约4GB的内存,直到进程结束。

如果我们将for行更改为

for (unsigned int idx=0; idx < numElem; idx++)
    voxelToPixel.at(0).push_back(idx);

记忆被释放。

在linux机器上使用gcc-4.8。我们使用htop来跟踪具有100 GB RAM的计算机上的内存使用情况。您将需要大约8 GB的RAM来运行代码。你能重现这个问题吗?关于为什么会发生这种情况的任何想法?

编辑: 我们已经看到在Mac中不会发生这种情况(gccclang)。此外,在linux中,如果我们调用foo两次(但第三次再次发生),则释放内存。

1 个答案:

答案 0 :(得分:27)

小分配(我认为默认情况下高达128kb)由进程内堆管理,并且在取消分配时不会返回给操作系统;它们将返回堆中以便在进程中重用。较大的分配直接来自操作系统(通过调用mmap),并在解除分配时返回到操作系统。

在第一个示例中,每个向量只需要为单个int分配足够的空间。您有一亿个小分配,其中任何一个都不会返回给操作系统。

在第二个例子中,随着向量的增长,它将进行多种不同大小的分配。有些小于mmap阈值,这些将保留在进程内存中;但是,因为你只对一个向量执行此操作,所以这不会是一个巨大的数量。如果您在填充之前使用resizereserve为每个向量分配所有内存,那么您应该会发现所有内存都返回给操作系统。