【问题标题】:Releasing memory from map. C++从地图中释放内存。 C++
【发布时间】:2011-03-03 10:50:56
【问题描述】:
std::map<int, int> * mp = new std::map<int, int>;
for(int i = 0; i < 999999; i++){
  mp->insert(std::pair<int, int>(i, 999999-i )); 
}
p("created");
//mp->clear();     -     doesn't help either
delete mp;
p("freed");

问题是:“删除 mp”没有任何作用。比较:

std::vector<int> * vc = new std::vector<int>;
for(int i = 0; i < 9999999; i++){
  vc->push_back(i); 
}
p("created");
delete vc;
p("freed");

释放内存。如何从 map 中释放内存?
PS: p("string") 只是暂停程序并等待输入。

【问题讨论】:

  • 你怎么知道“删除 mp 没有任何作用”?你为什么要更新地图?
  • 你怎么知道“删除 mp 没有任何作用”? - 我只看任务管理器(linux 中的“top”),我看到了应用程序使用的 RAM。你为什么要更新地图? - 只是试验。
  • 为什么要这样做?
  • 我不会将“检查应用程序使用的 RAM”称为可靠测试。如果您使用的是 Linux,请改用 valgrind

标签: c++ stl memory-leaks map


【解决方案1】:

应用程序使用的 RAM 并不是判断内存是否已被语义释放的精确方法。

释放的内存是可以重复使用的内存。但有时,您不会直接在操作系统报告为我们的应用程序使用的内存中观察到释放的内存。

你知道内存被释放是因为语言的语义是这样说的。

【讨论】:

  • 好吧,如果我迭代 99999999 程序将占用我计算机 RAM (4GB) 的 15%,所以我不是在谈论千字节的 RAM。如果我使用矢量,在删除 15% 的内存使用率后将下降到 0.0%。如果我使用地图,“删除”后我看不到任何区别。
  • @foret:这可能是因为 vector 必须使用单个连续分配,而 std::map 将使用许多小分配(每个键/值节点一个)。将大于 4KB 的分配直接交给操作系统是很常见的。
  • @foret:要在实践中看到这一点,只需将代码放入循环中并运行它,例如十次。在您的情况下,第一次循环迭代后不应发生进一步的内存增长。
【解决方案2】:

其实,如果下面的代码没有泄露的话:

{
  std::map<int, int> some_map;
}

以下代码也不应泄漏:

{
  std::map<int, int>* some_map = new std::map<int, int>();
  /* some instructions that do not throw or exit the function */
  delete some_map;
}

这适用于您使用new 的任何类型,只要类型写得好。 std::map 可能写得很好。

我建议您使用valgrind 来检查您的泄漏。我非常怀疑您观察到的是真实泄漏。

【讨论】:

  • STL 分配方案更有可能保留释放的内存以供将来分配:)
【解决方案3】:

要确定您是否正在释放内存,请尝试向对象的析构函数添加可观察的效果,然后...观察它们。 例如,创建一个自定义类而不是映射,该类在调用析构函数时发出输出。 像这样的:

#include <map>
#include <iostream>
#include <utility>

class Dtor{
        int counter;
    public:
        explicit Dtor(int c):counter(c) {std::cout << "Constructing counter: " << counter << std::endl; }
        Dtor(const Dtor& d):counter(d.counter) {std::cout << "Copy Constructing counter: " << counter << std::endl; }
        ~Dtor(){ std::cout << "Destroying counter: " << counter << std::endl; }
};

int main(){
    std::map<int, const Dtor&> * mp = new std::map<int, const Dtor&>;
    for (int i = 0; i < 10; ++i){
        mp -> insert(std::make_pair(i, Dtor(i)));
    }
   delete mp;
   return 0;
}

您将目睹删除指针会调用对象的析构函数,正如预期的那样。

【讨论】:

  • 如果你这样做,包含赋值运算符通常很有用(因为它无论如何都会生成)
  • @Martin:是的,这只是一个快速写下来的例子。 IIRC 我添加了复制构造函数只是为了在视觉上匹配析构函数。
【解决方案4】:

正如 Daniel 所提到的,应用程序使用的 RAM 不一定是内存泄漏的指标。 关于你注意到的关于向量的行为,向量保证内存布局是连续的,因此当你创建一个大小为 99999999 的向量时,所有 99999999 个元素都按顺序排列,将构成相当大的内存块。删除它肯定会影响进程大小。地图的行为不同(根据维基百科,它通常实现为自平衡二叉树)所以我猜删除它会导致进程内存空间中的碎片&可能是由于进程内存不会立即下降。 检测内存泄漏的最佳方法是使用像 valgrind 这样的工具,它可以清楚地指出问题所在。

【讨论】:

  • 虽然维基百科是一个很好的研究起点,但它不是一个引用作为参考的好地方(威尔士先生在这个问题上已经被引用了好几次)。这是一个引导您获得权威资源的好地方(在这种情况下是 c++ STL 容器的要求),它应该向您表明不需要将映射实现为平衡树)(尽管您认为大多数实现都是正确的请务必使用此方法。
  • @Martin - 同意,感谢您指出。一定会记住这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-29
  • 1970-01-01
  • 2017-12-22
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多