这是 std::vector 的正常行为吗?

Posted

技术标签:

【中文标题】这是 std::vector 的正常行为吗?【英文标题】:Is this normal behavior for a std::vector? 【发布时间】:2010-07-03 19:52:54 【问题描述】:

我有一个名为 OGLSHAPE 的类的 std::vector。

每个形状都有一个 SHAPECONTOUR 结构的向量,它有一个浮点向量和一个双精度向量。它还有一个大纲结构的向量,其中有一个浮点向量。

最初,我的程序使用 8.7 MB 内存启动。我注意到,当我开始填充这些内容时,例如添加双精度和浮点数,内存很快就变得相当高,然后趋于平稳。当我清除 OGLSHAPE 向量时,仍然使用了大约 19MB。然后,如果我再推大约 150 个形状,然后清除它们,我现在使用大约 19.3MB 的内存。从逻辑上讲,如果第一次从 8.7 上升到 19,那么下一次它会上升到 30 左右。我不确定它是什么。我以为这是内存泄漏,但现在我不确定。我所做的只是将数字推入 std::vectors,仅此而已。所以我希望能找回我所有的记忆。这是什么原因造成的?

谢谢

*编辑,好吧它的内存碎片 从分配很多小东西, 怎么解决?

【问题讨论】:

您在哪个平台上以及如何测量内存“使用情况”? “是什么原因造成的?” OGLSHAPE 中的内存泄漏很容易做到。 您有机会发布您的代码的简化版本吗? 你为什么要用 C 标签来标记一个关于 std::vector 的问题???对于我们这些想要回答 C 问题的人来说,这是一种痛苦。 使用 Windows 任务管理器验证内存使用情况 【参考方案1】:

调用 std::vector::clear() 不一定释放所有分配的内存(它取决于 std::vector 的实现)。这通常是为了优化以避免不必要的内存分配。

为了真正释放实例持有的内存,只需执行以下操作:

template <typename T>
inline void really_free_all_memory(std::vector<T>& to_clear)

    std::vector<T> v;
    v.swap(to_clear);


// ...
std::vector<foo> objs;

// ...
// really free instance 'objs'
really_free_all_memory(objs);

创建一个新的(空)实例并将其与您要清除的向量实例交换。

【讨论】:

我按照你刚才提到的 OGLSHAPE 向量做,我假设所有其他向量都调用它们的析构函数 @hkaiser,很好的提示。我想知道为什么不将此作为标准功能添加到 STL 中(类似于:std::vector::free_all_memory)。 @Evan:我认为v.shrink_to_fit()std::vector&lt;T&gt;().swap(v) 更容易理解。后者是一个习语,你必须学习和了解它才能理解该代码在做什么,而且它甚至不能保证可以工作。一个恰当命名的函数当然要好得多——这就是为什么下一个版本的标准实际上会有std::vector&lt;&gt;::shrink_to_fit() @sbi:我一般同意,但不幸的是,像v.shrink_to_fit() 这样的功能可能在一定程度上隐藏了操作成本。看到带有交换的复制构造函数可以很明显地看出发生了什么成本明智。【参考方案2】:

使用正确的工具来观察您的内存使用情况,例如(在 Windows 上)使用 Process Explorer 并观察 Private Bytes。不要查看虚拟地址空间,因为它显示了正在使用的最高内存地址。碎片化是两个值之间存在巨大差异的原因。

还要意识到您的应用程序和操作系统之间有很多层:

std::vector 不一定会立即释放所有内存(请参阅 hkaiser 的提示) C 运行时并不总是将所有内存返回给操作系统 操作系统的堆例程可能无法释放所有内存,因为它只能释放整个页面(4 KB)。如果 4KB 页面的 1 字节仍被使用,则无法释放该页面。

【讨论】:

【参考方案3】:

这里有一些可能的事情。

首先,内存在最常见的 C 和 C++ 运行时库中的工作方式是,一旦它从操作系统分配给应用程序,它就很少会返回给操作系统。当您在程序中释放它时,new 内存管理器会保留它,以防您再次要求更多内存。如果你这样做了,它会返还给你以供重复使用。

另一个原因是向量本身通常不会减小它们的大小,即使你 clear() 它们也是如此。他们将“容量”保持在最高水平,以便更快地重新填充它们。但是,如果向量被销毁,那么该内存将返回到运行时库以再次分配。

因此,如果您不销毁向量,它们可能会在内部为您保留内存。如果您在操作系统中使用某些东西来查看内存使用情况,它可能不知道有多少“空闲”内存在运行时库中等待使用,而不是返回给操作系统。

您的内存使用量略有增加(而不是根本不增加)的原因可能是由于碎片。这是一种复杂的切线,但足以说明分配大量小对象会使运行时库在需要时更难找到大块。在这种情况下,它不能重用它周围已经释放的一些内存,因为它是很多小块的。所以它必须去操作系统并请求一个大块。

【讨论】:

和内存泄漏一样吗? 一般不用担心内存碎片。这是很自然的,很少会成为一个严重的问题。我概述的所有内容都不是内存泄漏,只是在不做更复杂的事情的情况下检测是否存在内存泄漏的障碍。使用跟踪内存的调试器,它会告诉你是否有泄漏。

以上是关于这是 std::vector 的正常行为吗?的主要内容,如果未能解决你的问题,请参考以下文章

大型数组、std::vector 和堆栈溢出

PHP - PDO OOP MySQL 数据库连接被重复,这是正常行为吗? [复制]

这是 std::vector 和 std::shared_ptr 内存泄漏的错误吗?

VBO的std :: vector偏移计算?

奇怪的 std::vector::reverse_iterator 和字符串行为

GCC的std :: sort与lambdas的不稳定行为