【问题标题】:Huge std::vector<std::vector> does not release all memory on destruction [duplicate]巨大的 std::vector<std::vector> 不会在销毁时释放所有内存 [重复]
【发布时间】:2014-11-19 03:56:06
【问题描述】:

当使用一个非常大的向量向量时,我们发现部分内存没有被释放。

#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 两次(但第三次再次发生),内存就会被释放。

【问题讨论】:

  • 你能用一个更小的例子重现这个吗?
  • @stefan 小了怎么办?元素数量?我在 2^25 看到过这种情况,但对于较小的情况很难说。

标签: c++ memory vector


【解决方案1】:

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

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

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

【讨论】:

  • 每个1&lt;&lt;27 内部向量仅包含一个int,因此resize/reserve 在这种情况下无济于事。外部向量已经有一个resize
  • @MarkRansom:哎呀,你说得对,我看错了代码。
  • @quimnuss:您可以使用自定义分配器将所有内存分配到一个巨大的块中,将其分配给向量,并在使用后将其全部释放; Boost.Pool 可能有用。您可以尝试malloc_trim - 但这只能将内存释放到堆的第一个“已使用”部分。一旦堆被碎片化,使用过的内存块散布在其中,您就无能为力了。如果操作系统内存不足,那么它就会释放可怕的 oom-killer,生活变得非常可怕。
  • @quimnuss 无需强制进程释放未使用的堆内存。操作系统已经可以拿走物理内存,而虚拟内存很便宜。
  • 在 Linux 中,没有仔细分配内存。您写入允许用于堆的虚拟内存区域,Linux 会找到一些 RAM 来支持它。您可以通过使用brk 系统调用设置数据段的大小来通知Linux 您已经使用完整个内存区域。 C++ 分配器将为您处理这个问题。如果你不释放内存,但也有一段时间不使用它,Linux 会将它分页到磁盘,让其他进程或磁盘缓存使用 RAM。
猜你喜欢
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
  • 2018-07-06
  • 2020-10-11
  • 1970-01-01
  • 2011-08-30
相关资源
最近更新 更多