CArray无法释放内存

3

我有一个非常简单的问题,但是我似乎被困住了。请查看以下代码:

CArray<double, double&> arr;
arr.SetSize(50000);

for(int i =0; i< 50000; i++)
{
    arr[i] = (i+2)*3.14f;
}

arr.RemoveAll();

我认为在调用RemoveAll()之后,内存应该会被释放,但实际上似乎并没有发生。为了检查内存占用情况,请打开任务管理器并观察你的exe内存。在调用arr.SetSize()时,它会增加,但即使arr超出作用域,它也永远不会减少。有人能解释一下吗?
3个回答

7

第一件事:

任务管理器 != 内存分析器

操作系统(或更具体地说是C运行时系统)肯定会缓存您分配的某些内存。

请注意,操作系统通常不是愚蠢的 - 如果这是应用程序未使用的内存,并且操作系统需要更多内存来满足某个其他程序的内存需求,则会相应地进行分配。但如果情况不是这样(大部分情况都不是),那么将其保留供应用程序在需要时重用是一种获胜的策略。

由于这些优化,您实际上无法使用任务管理器准确查看应用程序的内存使用情况。

第二件事:

与所有非愚蠢的动态数组类一样,CArray 即使在删除所有元素后也不会释放支持数组的内存,以防您需要再次使用内存缓冲区。删除底层内存缓冲区只会浪费处理器周期,因为您可能需要在 CArray::RemoveAll() 之后紧接着进行下一个 CArray::Append() 调用,而这会导致需要浪费更多的处理器周期重新分配另一个缓冲区来处理。

如果您真的想摆脱那些额外的空间,请使用 CArray::FreeExtra()。请注意,该函数可能涉及分配一个新缓冲区并将元素复制到新缓冲区中,然后删除旧缓冲区。


我尝试了FreeExtra,但它仍然不起作用。我知道任务管理器不是内存分析器,但那是最容易获得的东西。还有其他建议吗? - Aamir
@In Silico,为什么FreeExtra需要分配一个新缓冲区来减小大小?当然,如果它使用realloc来缩小内存使用量,您最终会得到一个碎片化的堆,您可以避免额外堆空间的内存开销以及复制和调用构造函数和析构函数的时间开销。 - SmacL
@Shane MacLaughlin:这是一种可能的工作方式。但是文档没有指定也不保证它能像那样工作(我已经重新措辞了句子)。而且它并不是普遍适用的;例如,std::vector 几乎肯定需要分配一个新缓冲区并删除旧缓冲区,因为分配器接口没有 realloc() 能力。 - In silico
@Aamir,无论CArray在做什么,堆管理器何时向操作系统返回内存是没有保证的。通常情况下,如果您在数组之后分配了任何内容,则堆将会被碎片化,因此虽然内存可能对程序可用,但它不会对操作系统或其他程序可用。查询heapwalk以获取更多信息。 - SmacL
@In Silico,刚看了一下MFC数组类源代码,你说得很对,是我的错。我几年前重写了自己的数组类,因为MFC实现速度慢且贪婪。 - SmacL

2
正如其他人所说,RemoveAll不会释放CArray分配的内存,您需要调用CArray::FreeExtra。
您说在CArray的析构函数之后内存没有下降。CArray::SetSize从堆中分配内存,CArray的析构函数释放它。但这并不意味着堆内存被返还给操作系统。
当您使用MFC时,建议定期在调试器中启动程序。MFC调试应用程序在MFC清理期间打印出它们的内存泄漏(基于new)。

1

在 RemoveAll 操作后,您需要调用 FreeExtra 以释放内存。我猜这是为了避免堆碎片。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接