【问题标题】:releasing memory to os after using large number of unique_ptr - c++使用大量unique_ptr后向操作系统释放内存 - c++
【发布时间】:2013-03-30 05:14:32
【问题描述】:

无法弄清楚发生了什么错误。

这是一个简单的代码

size_t n_elem = 30000000; //careful! will allocate 1GB with unique_ptr
vector<unique_ptr<double> > tmp;
tmp.resize(n_elem);
for(size_t i=0; i<n_elem; i++){
   tmp[i] = unique_ptr<double>(new double((double)i));
}
tmp.clear();
//Some answers in Stack overflow seems to suggest this technique to force
//freeing, but does not seem to work.
vector<unique_ptr<double> > tmp1;
tmp.swap(tmp1);

当这完成时。系统监视器仍然显示 948 MB 已分配,当它应该显示内存使用量激增然后释放所有内容时,对吗?即使将 sn-p 括在大括号 {...} 中以确保范围,我也看不到任何改进。一旦分配了内存,它似乎直到程序结束才释放。

怀疑与向量有关,我尝试了以下代码,但行为相同。

{
    size_t n_elem = 30000000;
    unique_ptr<double>* tmp = new unique_ptr<double>[n_elem];
    for(size_t i=0; i<n_elem; i++)
        tmp[i] = unique_ptr<double>(new double((double)i));
    delete[] tmp;
}

然而,

size_t n_elem = 30000000;
double *d_tmp;
d_tmp = new double[n_elem];
for(size_t i=0; i<n_elem; i++)
{
    d_tmp[i] = (double)i;
}
delete[] d_tmp;

此代码显示内存使用量上升到大约 260 MB,然后随着数组被删除而减少。

我尝试使用类而不是双精度类,并计算使用静态变量调用析构函数的次数。在这两种情况下,析构函数都被调用了正确的次数。

我无法弄清楚我使用 unique_ptr 的方式出了什么问题,以及为什么它在销毁后没有返回分配给操作系统的内存。 cpp 参考似乎表明这应该在 uniue_ptrs 被破坏时自动发生。如何让 unique_ptr 释放它分配的内存并将其返回给操作系统?

这发生在带有 ubuntu 12.04 和 qtcreator qt 项目的 gcc 4.6 上。

【问题讨论】:

  • 您正在创建两个 unique_ptr 数组并且只删除一个?
  • @yngum,不,即使在第二个代码 sn-p 中也会出现问题。 sn-ps 是独立运行的。第一个 sn-p 将一个空向量与当前向量交换。
  • 第二个 sn-p 也为每个元素创建两个 unique_ptr
  • @yngum,哦。怎么会这样?你建议我怎么改?我只想检查许多 unique_ptrs。实际代码将有一个包含两个 unique_ptrs 的类,但该类有许多实例。
  • @sri - 这只是虚拟内存。在程序结束之前,运行时可能不关心将地址空间还给操作系统。物理内存仍然可供操作系统(和其他程序)使用。

标签: c++ unique-ptr


【解决方案1】:

稍微改变你的代码

tmp.clear(); // it is not needed
{
  //Some answers in Stack overflow seems to suggest this technique to force
  //freeing, but does not seem to work.
  vector<unique_ptr<double> > tmp1;
  tmp.swap(tmp1);
}
// measure memory here

【讨论】:

    【解决方案2】:

    尝试改变这个:

    tmp[i] = unique_ptr<double>(new double((double)i));
    

    收件人:

    tmp[i].reset(new double((double)i));
    

    另外,我不会假设知道您打算做什么(或知道更好),但如果您要复制的是动态分配的双精度数组,则不需要制作它持有unique_ptr的。直接使用vector&lt;double&gt;。否则,您将为要存储的每个 double 创建一个 unique_ptr。这与连续分配的数组(从而利用局部性/缓存)相同。这似乎会产生不必要的开销。

    【讨论】:

    • 谢谢。但这似乎也不能解决问题。这只是一个孤立的案例。唯一指针实际上在一个类中用于基于树的表示,它可能有一些未知数量的孩子。我使用 unique_ptr 不用担心对象被销毁时的清理,但我注意到内存似乎永远不会下降。我怀疑类中的代码,但事实证明我能够在这些代码 sn-ps 中复制相同的行为。
    • @sri 您是否真的在检查您提供的示例代码?还是您在测试中使用unique_ptr&lt;SomeClass&gt;?我刚刚通过 valgrind 运行它,它说没有内存泄漏。作为旁注,作为对您的评论的回应,我并不是要对此进行纠缠,但您似乎应该能够在课堂上使用vector&lt;NodeClass&gt; children;。我认为不需要unique_ptrs
    • 我正在检查问题中的示例代码。我也检查过没有内存泄漏,但是,它似乎直到程序退出后才将内存释放给操作系统,这可能很麻烦。我会试试矢量的想法,看看效果如何。
    • @sri 确实我明白你的意思,但是,据说 valgrind 会检查整个程序的内存泄漏。例如,如果我更改代码以声明一个数组 (new double[]) 而不是 delete 它,它会告诉我,即使在程序结束时操作系统会如您所说的那样回收内存。跨度>
    • @sri 是的,这很奇怪。只是在 gdb 中遍历它并没有注意到任何泄漏。内存在clear() 之后立即被回收。
    【解决方案3】:

    通常,内存管理器会在需要时向操作系统请求更多内存,但不要试图在完成后将其还给操作系统。后者通常很难做到,因为堆的使用往往会飞溅,并且在知道何时不再需要 OS 大小的内存块时涉及到太多的簿记。此外,即使内存管理器执行此操作,操作系统也可能会将该内存分配给您的程序,直到其他地方需要它,因此报告内存使用情况的操作系统工具并不能反映您的应用程序实际使用的内容。

    这里真正的问题是这种多余的内存是否会造成问题;通常,操作系统通过交换到磁盘来管理大型工作集;未使用的内存块会使后备存储变得混乱,但不要占用 RAM。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-03
      • 2010-11-17
      • 2020-04-09
      • 2019-11-02
      相关资源
      最近更新 更多