Visual C++ 矢量擦除会增加内存使用量?
Posted
技术标签:
【中文标题】Visual C++ 矢量擦除会增加内存使用量?【英文标题】:Visual C++ vector erase increases memory usage? 【发布时间】:2014-12-10 17:44:38 【问题描述】:这是我在 *** 上的第一个问题。我在很大程度上寻找了以下代码行所遇到的原因:
unsigned long long _mem1= getUsedVirtualMemory();
vector.erase(vector.begin() + _idx);
contained= false; // don't stop the loop
_idx--; // the removed object has redefined the idx to be consider.
_mem1 = getUsedVirtualMemory() - _mem1;
if (_mem1 > 0) printf("Memory - 2 mem1: %lu\n" , _mem1);
我的程序中有大量内存消耗,经过密集的调试会话、一些 printfs 和耗时的分析,我到达了这一点:
getUsedVirtualMemory 使用以下代码实现:
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc));
SIZE_T virtualMemUsedByMe = pmc.PrivateUsage;
return virtualMemUsedByMe;
获取进程分配的虚拟内存量;向量是对象的向量(不是指针)。
在大多数情况下,向量的擦除方法似乎按预期工作,但在某些情况下,该向量的擦除方法似乎增加了进程使用的内存而不是释放它。我在代码周围的很多情况下都使用 Windows 系统函数 GetProcessMemoryInfo 来调试这个问题,它似乎返回了已用虚拟内存的实际值。
我正在使用 Visual Studio C++ 2010 Professional。
如果需要更多信息,请告诉我。
感谢您的任何回复。
更新:
你在回复中写的一切都是正确的,我忘记了以下细节:
-
我已经知道向量具有大小(元素的实际数量)和容量(分配的用于存储元素的插槽)
我已经知道擦除方法不会释放内存(我查找了很多关于该方法的文档)
最后,我稍后会在该向量中添加其他元素,因此我不需要缩小该向量。
实际的问题是,在这种情况下,最后一行代码中“_mem1”的值显示了1.600.000字节的差异:内存的不合理增加,而我预计是0字节。
此外,在操作后已用内存的值小于第一个的情况下,我希望在 Is unsigned integer subtraction defined behavior? 中解释的内容会有一个非常大的数字
我得到一个大于 0 但相对较短的值,而不是预期的结果。
为了更好地了解问题的发生率,在这段代码上迭代数千次,意外分配了大约 20 Gb 的虚拟内存。
【问题讨论】:
释放运行时库函数的分配并不一定会将任何东西释放回操作系统。内存分配器通常会保留一个可用块池来满足以后的分配,而不会浪费时间将内存交还给操作系统以便稍后重新请求它。因此,您没有看到内存使用量减少也就不足为奇了。 我绝对同意你的观点,但我无法证明“为什么擦除操作应该分配大约 1.5Mb 的额外内存?” 【参考方案1】:一个向量有:
size()
,表示容器中有多少活动元素
capacity()
,告诉内存中保留了多少元素
erase()
将大小更改为零。它不会释放分配的内存容量。
您可以使用 shrink_to_fit() 确保容量减少到大小。
使用resize()
更改大小或使用reserve()
更改容量可能会在必要时增加分配的内存,但如果新的大小/容量低于现有容量,则不一定会释放内存。
【讨论】:
不幸的是,shrink_to_fit()
是一个不具约束力的请求,所以如果你需要确定性,你会被 this old swap trick 卡住。
没错,这是一个非绑定请求。然而,OP 使用的 MSVC 2010 实现似乎确保了请求得到满足:msdn.microsoft.com/fr-fr/library/dd647619%28v=vs.100%29.aspx【参考方案2】:
这是因为擦除不会释放内存,它只是擦除元素。看看Herbs。
(真正)释放你可以做的记忆(来自参考链接):
“Shrink-To-Fit”向量或双端队列的正确方法
那么,我们是否可以编写代码来缩小向量“以适应”,使其容量刚好足以容纳包含的元素?明明reserve()不能胜任,幸好确实有办法:
vector<Customer>( c ).swap( c );
// ...now c.capacity() == c.size(), or
// perhaps a little more than c.size()
【讨论】:
不错的技巧:您创建一个临时未命名的空向量并进行交换。由于匿名临时在创建它们的语句结束时自动销毁,因此内存被释放。但是阅读起来有点棘手。与更容易理解的更简单的c=vector<Customer>();
有性能差异吗?
@Christophe:实际上,这个示例创建了一个向量的副本,然后将其与原始向量交换。希望副本的 capacity()
实际上比原始的少,并且值得暂时增加内存使用和元素复制。
对,它基本上是“修剪”本身:)
@Blastfurnace 是的,但是由于之前删除的所有元素都将其修剪为空。这就是为什么我问分配一个空向量是否更简单。
@Christophe 这不是清空向量,而是释放向量在增长时(可能)保留的“额外”内存。看看参考,它应该解释这个成语:)【参考方案3】:
vector.ersase()
只保证从向量中删除元素,不保证减少底层数组的大小(因为该过程相当昂贵)。 IE:它只是清零数据,不一定释放它。
如果您需要一个仅与其包含的元素一样大的向量,请尝试使用vector.resize(vector.size())
【讨论】:
您的最后一个示例不会释放任何内存,resize()
具有相同或更小的size()
不会减少capacity()
。
感谢您的回复,是的,我同意您的看法,但为什么在很多情况下它会分配大约 1.5Mb 的内存(据说丢失且数量不固定)?我预计擦除后分配的内存差异为0。请参考问题的更新。【参考方案4】:
IIRC 在 Windows 上的 Debug 版本中,new
实际上是 #defined
为 DEBUG_NEW
,这会导致(除其他外)内存块未被实际释放,而只是标记为“已删除”。
发布版本是否有相同的行为?
【讨论】:
是的,不幸的是,我在发布版本中得到了相同的行为,它只分配了更少的内存(大约 50%)。【参考方案5】:难题的一部分可能是 std::vector 无法从底层内存缓冲区中删除条目,如果它们不在缓冲区的末尾(你的不是),因此保留的条目被移动 - 可能到一个完全不同的缓冲区。由于您正在擦除第一个元素,因此允许 std::vector 分配一个 additional 缓冲区将剩余元素复制到,然后在复制后丢弃旧缓冲区。因此,您最终可能会同时使用两个缓冲区,并且您的内存管理器可能不会将丢弃的缓冲区返回给操作系统,而是保留内存以便在后续分配中重新使用它。这可以解释单个循环迭代的内存使用量增加。
【讨论】:
以上是关于Visual C++ 矢量擦除会增加内存使用量?的主要内容,如果未能解决你的问题,请参考以下文章