700,000个向量元素的突然内存峰值

时间:2015-04-01 22:04:26

标签: c++ memory memory-management vector

所以我在我的程序中运行了一个内存使用测试,我在每个帧(~60fps)中每个向两个单独的向量添加20个元素。我预计在某些时候我会开始看到内存泄漏,但是在某个临界点之前内存使用量保持不变。在新的高原上,大约有700,000个元素飙升,然后再次平稳。

我有一种感觉,这与此时自动增加的矢量分配有关,但我不确定,也无法在网上找到任何东西。它也不能真正解释为什么在这一点上分配了如此多的额外内存(CPU上的专用字节数从~800跳到~900,系统GPU内存从~20跳到~140)。以下是CPU和GPU的Process Explorer图表:

enter image description here

注意:CPU和GPU使用率的下降来自于我在看到峰值后暂停程序。

任何人都可以向我解释这个吗?

编辑:这是一个更简单,更通用的测试:

enter image description here

总使用量明显低很多,但想法一致。

3 个答案:

答案 0 :(得分:5)

当您向空向量添加元素时,它将通过new为多个元素分配足够的空间。像16也许。这样做是因为将数组调整到较大的缓冲区很慢,因此它分配的数量超过了它的需要。如果它为16个元素分配空间,这意味着你可以在需要再次调用new之前再推回15个元素。每次它增长得更多。如果你有500个元素(并且它不在房间里)并再推回一个,它可能会分配750个空间。或者甚至可能是1000个。或者2000个。充足的空间。

事实证明,当您(或向量)调用new时,您可以从程序的内存管理器中获取此信息。如果程序的内存管理器没有足够的可用内存,它将向操作系统询问大量内存,因为操作系统调用本身很慢,并且处理页面映射的速度很慢。因此,当vector要求200个字节的空间时,程序的内存管理器可能实际抓取65536个字节,然后只给出200个向量,并保存剩余的到new的下一个呼叫的65336个字节。因此,在您不得不再次打扰操作系统之前,您(或矢量)可以多次调用new,事情就会很快发生。

但这会产生副作用:操作系统实际上无法确定程序真正使用的内存量。它只知道你从中分配了65536,所以它会报告。当您向后推送向量中的元素时,最终向量会耗尽容量,并从程序的内存管理器中请求更多内容。而且它越来越多,并且操作系统报告相同的内存使用情况,因为它无法看到。 最终内存管理器的容量耗尽,并要求操作系统提供更多内容。操作系统分配另一个巨大的块(65536?131072?),你会看到内存使用量突然大幅增加。

未设置发生这种情况的向量大小,它取决于还分配了什么,以及分配和取消分配的顺序。即使你delete仍然影响事物的事情,它也非常复杂。此外,矢量增长的速率取决于您的库实现,以及程序的内存管理器从操作系统获取的内存量也会因我甚至不知道的因素而有所不同。

我不知道为什么GPU的内存会飙升,这取决于你对程序的处理方式。但请注意,GPU内存总量较少,完全有可能它比“私有字节”的量增长。

答案 1 :(得分:1)

向量使用动态分配的数组来存储它们的元素。这个数组可能需要重新分配,以便在插入新元素时增大大小,这意味着分配一个新数组并将所有元素移动到它。就处理时间而言,这是一项相对昂贵的任务,因此,每次将元素添加到容器时,向量都不会重新分配。相反,矢量容器可以分配一些额外的存储空间以适应可能的增长,而因此容器的实际容量可能大于包含其元素所需的存储容量(即其大小)这解释了你的高原。容量扩大是通过加倍当前容量来完成的。可能是在有限数量的倍增之后,其容量翻了四倍。 这将解释你的尖峰。

答案 2 :(得分:0)

矢量大小和容量

通过分配比以往更多的内存来获得良好的性能。

尺寸()

返回当前元素数

空()

返回容器是否为空(相当于0 == size()但更快)

容量()

返回没有重新分配的最大可能元素数

储备(NUM)

扩大容量,如果还不够

矢量的容量很重要,因为 重新分配使元素的所有引用,指针和迭代器无效。 通过将所有元素移动到新堆位置来重新分配需要时间。 重新分配大小增量取决于实际的矢量实现。

使用reserve(num)的代码示例:

std::vector<int> v1;  // Create an empty vector    
v1.reserve(80);       // Reserve memory for 80 elements