我有以下循环,从这里的实现中弹出一个C ++并发队列。 https://juanchopanzacpp.wordpress.com/2013/02/26/concurrent-queue-c11/
while (!interrupted)
{
pxData data = queue->pop();
if (data.value == -1)
{
break; // exit loop on terminating condition
}
usleep(7000); // stub to simulate processing
}
我正在使用CentOS7中的系统监视器查看内存历史记录。 在读取队列中的值之后,我试图释放队列占用的内存。但是,随着以下while循环运行,我不会看到内存使用率下降。我已经确认队列长度确实下降了。
但是,当遇到-1并且循环退出时,确实会失效。 (程序仍在运行)但我无法做到这一点,因为在我们睡觉的地方,我想做一些密集的处理。
问题:为什么数据占用的内存不会被释放? (根据系统监视器)当变量超出范围时,堆栈分配的内存不应该被释放吗?
结构定义如下,并在程序开头填充。
typedef struct pxData
{
float value; // -1 value terminates the loop
float x, y, z;
std::complex<float> valueData[65536];
} pxData;
它填充了~10000 pxData,大致翻译为5GB。系统只有~8GB。 因此,在系统中进行其他处理时,内存可以自由编辑,这一点非常重要。
答案 0 :(得分:6)
这里有一些事情可以发挥作用。
首先,你需要明白,因为你的程序是&#34;使用&#34; 5 GB的内存并不意味着其他程序只剩下3 GB的RAM。虚拟内存意味着那些5 GB可能只有1 GB的实际&#34;驻留&#34;数据,其他4 GB实际上可能在磁盘上,而不是在RAM中。因此,重要的是要了解常规设置大小&#34;而不是&#34;虚拟尺寸&#34;当你在看你的节目时。请注意,如果您的系统实际上在RAM上运行不足,那么操作系统可能会缩小某些程序的RSS,以及#34;分页&#34;他们的一些记忆。所以不要过于担心&#34; 5 GB&#34;出现在系统监视器中 - 担心是否存在真正的具体性能问题。
第二个方面是您从队列中删除项目时虚拟大小不会减少的原因。我们可以猜测,您可以通过逐个malloc
或new
创建它们,然后将它们推送到队列的后面,将这些元素放入队列中。这意味着您分配的第一个元素将首先从队列中出来。而这反过来意味着当你耗尽了90%的队列时,你的内存分配可能如下所示:
[program|------------------unused-------------------|pxData]
这里的问题是,在现实世界中,仅仅因为你free
或delete
某事并不意味着操作系统会立即回收那些记忆。实际上,它可能无法收回任何未使用的跨度,除非它们位于&#34; end&#34; (即最近分配的)。由于C ++没有垃圾收集,并且在未经您同意的情况下无法在内存中移动项目,因此您最终会遇到这个重要的漏洞&#34;在你的程序的虚拟内存中。该漏洞将用于满足未来的内存分配请求,但如果您还没有,那么它只是坐在那里,直到队列完全为空:
[program|------------------unused--------------------------]
然后系统能够缩小您的虚拟地址空间:
[program]
它将您带回到您开始的地方。
如果你想&#34;修复&#34;这个,一个选项是在&#34;反向&#34;中分配你的记忆,即把最后的项目分配到队列的前面。
另一个选择是通过mmap
为队列分配元素,例如, Linux将自动执行大型分配。&#34;您可以通过使用M_MMAP_THRESHOLD
调用mallopt(3)
并将其设置为比结构大小稍小一些来更改此阈值。这使得分配彼此独立,因此OS可以单独回收它们。这种技术甚至可以应用于现有程序而无需重新编译,因此如果您需要在无法修改的程序中解决此问题,这通常很有用。
答案 1 :(得分:4)
C ++实现会调用某些operator delete
来释放动态分配(使用一些operator new
)内存。在多个C ++标准库中,new
调用malloc
和delete
调用free
。
(我主要关注Linux的观点,但原则在其他操作系统上类似)
但malloc
(或::operator new
)有时通过系统调用向操作系统内核询问更多内存,更改virtual address space mmap(2) ,free
(或::operator delete
)经常只是将已发布的内存区域标记为将来对malloc
的调用重新可用(或new
)
所以从内核的角度来看(例如,见/proc/
,见proc(5) ...),虚拟地址空间没有变化,内存仍然消耗,即使在内部应用程序将其标记为“已释放”,并将在以后的某个分配中重复使用(通过将来调用malloc
或new
)
大多数C ++标准containers使用堆数据内部。特别是您的本地(堆栈分配)std::map
或std::vector
(或std::deque
)变量将调用new
&amp;内部数据delete
。
struct pxData
完全 65536 使用 valueData
广告位,否则我建议使用一些std::vector
std::vector<std::complex<float>> valueData;
并相应地改进您的代码。您可能需要执行一些valueData.reserve(somesize);
和/或valueData.resize(somesize);
和/或valueData.push_back(somecomplexnumber);
等等。