【问题标题】:How to release unordered_map memory?如何释放 unordered_map 内存?
【发布时间】:2017-06-26 03:00:32
【问题描述】:

问题

我发现使用 map.clear()map.rehash(0) 不会从我的 RAM 中释放预期的内存。

我用以下代码创建了一个程序:

int main() {
    std::unordered_map<std::string, int> m1;
    std::unordered_map<std::string, int> m2;
    
    // fill the maps m1 and m2 with 5 million elements

    // bucket_count = 5098259; size = 5000000
    
    // now I clear m1
    m1.clear();
    m1.rehash(0);
      // m1: bucket_count = 2; size = 0
      // m2: bucket_count = 5098259; size = 5000000

    // now I clear m2
    m2.clear();
    m2.rehash(1);
      // m1: bucket_count = 2; size = 0
      // m2: bucket_count = 2; size = 0
}

根据程序每一步之后的bucket_countsize,似乎有很多空间正在从RAM中释放出来。但是,检查与系统监视器一起使用的 RAM,我得到以下 evolution

问题

即使在清除和重新散列两个映射之后,内存也只有在程序完成时才会释放。实际上,内存释放很少,但我不确定这是否可以在图片中看到。怎么可能?

我怎样才能真正从 RAM 中释放内存?

上下文

我正在使用动态规划算法来解决 Linux 中 4Gb RAM 的旅行商问题。由于内存不足而崩溃,所以我正在尝试优化程序使用的内存。

经过一些改进后,我保留了两个unordered_map

  • costs:存储上一次迭代的值
  • new_costs:在该迭代期间构建。

迭代结束后,我交换它们以便 costs 具有新的计算值,我清除 new_costs trying 以释放内存并开始构建下一个迭代。

代码是这样的:

std::unordered_map<std::string, int> costs(5098259);
std::unordered_map<std::string, int> new_costs(5098259);

for (int m = 1; m <= n; m++) {
  new_costs.clear();
  new_costs.rehash(5098259);

  while (something) {
    // I build the content of new_costs based on the content of costs
  }

  std::swap(costs, new_costs);
}

我最多只能分别在其中存储 2496144 和 2704156 个元素。这使得同时存储总共 5200300 个元素。假设键是一个最多包含 70 个字符 --71 个字节的字符串,而存储的值是一个浮点数 --4 个字节--,我将存储大约 380 Mb。

我知道unordered_map 的内存效率不高,但程序完全使用了我的 RAM 和交换内存,所以我一定遗漏了一些东西。


**为可能的重复编辑

How do I force my std::map to deallocate memory used?

我用C++有一个多星期了,不知道std::mapstd::unordered_map的内存管理是否一样,或者两者之间是否存在相关差异。

我也不知道deallocate的意思是不是和释放一个对象使用的内存完全一样,所以有更多的RAM可用。

如果两者相同,我基本上问的是同一个问题。但是,无论如何,该问题的答案是解释内存保留背后的原因,而不是提供解决方案。

【问题讨论】:

  • 你试过用空容器交换它吗?
  • 运行时可能会保留内存以供以后可能重用,而不会将其返回给操作系统。特别是如果您分配了许多小内存单元。
  • 确实,实际上我很高兴它通过设计保留了它的内存,并且对于某些容器保留的实现正是这样做的。在许多情况下,内存分配/释放太慢了。如前所述,只需在此处使用 std::swap 即可真正释放您的内存。
  • 我很惊讶这不是重复的,但我找不到合适的匹配。
  • 您的图表显示您的内存消耗有限。您的评论声称您的内存无限增长,导致处决。这两种说法不能同时成立。请判断哪一个是正确的。如果图表是正确的,那么你做的一切都是正确的,没有什么可担心的。如果评论是正确的,那么你做错了什么。我们无法确切知道是什么,但很可能不是您的想法。发布您的整个程序,以便我们识别和讨论未知的错误。

标签: c++ memory memory-management memory-leaks


【解决方案1】:

您需要担心三个级别的内存使用情况,而您的进程内存占用可能会显示错误的级别。

  1. 使用中的内存

    容器中正在使用的东西。很简单。

  2. 每个容器的分配器

    您的容器有一个分配器。这可能决定分配比请求更大的块,并且还可能保留已释放的内存以供以后重用。这两者通常都会以内存使用为代价来提高速度。

    您可能可以使用 交换技巧 释放此缓存:创建一个空的临时容器并 std::swap 它们。这也移动了分配器,这意味着当临时超出范围时,任何旧的缓存存储都将被释放。

    无论如何,对于释放的某些值:内存可能会返回到您的...

  3. 进程范围的动态分配器(通常是“堆”)。

    这可能出于完全相同的原因决定将内存缓存在您的进程中。没有义务将该内存返回给系统(直到您的进程退出)。

强制您的程序将内存返回给系统的唯一方法是编写您自己的分配器,使用适当的系统调用显式地执行此操作。

在具有虚拟内存的系统上,推理程序的内存使用情况的明智方法是担心使用了多少内存,而不必过多担心其虚拟地址空间的大小.

注意。如果尽管您清除和重复使用容器,内存使用继续增长,您要么有泄漏,要么正遭受内存碎片的困扰。这确实是个问题。当内存使用在运行过程中没有减少,这是完全正常的,无需担心。

【讨论】:

  • 感谢您的详细回答。我认为现在我将避免编写自己的分配器,因为这对于我的 C++ 水平来说听起来有点复杂。我的内存使用问题是我很容易达到 4GB 的 RAM,然后使用交换内存会减慢一切。如果我不打算使用这些数据,为什么要保留它?我不能在这里使用newdelete 吗?会怎么样?
  • 我同意在这种情况下编写自己的系统级分配器可能不是要走的路。但是,您的图表表明内存正在被很好地重用:确认,构建m1&m2,清除它们,然后再做一次。如果内存使用量没有第二次增加,则没什么问题。你可能想找到一些方法来减少内存使用,但没有任何东西被泄露。
【解决方案2】:

我们从像您这样的 stl 容器中释放内存的方法是将它们与空容器交换。您可以使用此模板:

template<typename T>
inline void freeContainer(T& p_container)
{
    T empty;
    using std::swap;
    swap(p_container, empty);
}

而不是调用:

new_costs.clear();

你会打电话

freeContainer(new_costs);

【讨论】:

  • 感谢您的代码。我已经复制粘贴了它,使用clear()rehash() 时使用的RAM 的结果等同于使用freeContainer()。我尝试过使用newdelete,但我无法编译我的程序。有什么建议吗? :)
  • 如何测量 RAM 使用率?
猜你喜欢
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
相关资源
最近更新 更多