堆栈内存未释放
Stack memory not released
我有以下循环,它从这里的实现中弹出我拥有的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 并且循环退出时,它确实会下降。(程序仍在运行(但我不能有这个,因为usleep在哪里,我想做一些密集的处理。
问:为什么数据占用的内存没有释放?(根据系统监视器(当变量超出范围时,堆栈分配的内存不是应该释放吗?
结构定义如下,并在程序开始时填充。
typedef struct pxData
{
float value; // -1 value terminates the loop
float x, y, z;
std::complex<float> valueData[65536];
} pxData;
它填充了~10000 pxData,大致转换为5GB。系统只有 ~8GB。因此,释放内存以在系统中执行其他处理非常重要。
这里有一些事情在起作用。
虚拟内存
首先,您需要了解,仅仅因为您的程序"使用"了 5 GB 的内存并不意味着只剩下 3 GB 的 RAM 留给其他程序。 虚拟内存意味着这 5 GB 可能只是 1 GB 的实际"驻留"数据,另外 4 GB 实际上可能在磁盘上而不是在 RAM 中。 因此,在查看程序时,查看"驻留集大小"而不是"虚拟大小"非常重要。 请注意,如果您的系统实际上内存不足,操作系统可能会通过"分页"某些程序的某些内存来缩小某些程序的 RSS。 因此,不要太担心系统监视器中出现"5 GB" - 如果您有实际,具体的性能问题,请担心。
堆分配
第二个方面是为什么您的虚拟大小不会随着您从队列中删除项目而减小。 我们可以猜测,您将这些元素放入队列中,方法是逐个创建malloc
或new
,然后将它们推到队列的后面。 这意味着您分配的第一个元素将首先从队列中出来。 这反过来意味着,当您耗尽了 90% 的队列时,您的内存分配可能如下所示:
[program|------------------unused-------------------|pxData]
这里的问题是,在现实世界中,仅仅因为您free
或delete
某些东西并不意味着操作系统会立即回收该内存。 事实上,它可能无法回收任何未使用的跨度,除非它们位于"末端"(即最近分配(。 由于C++没有垃圾回收功能,并且未经您的同意不能在内存中移动项目,因此您最终会在程序的虚拟内存中出现这个大"漏洞"。 该洞将用于满足未来的内存分配请求,但如果你没有,它就坐在那里,直到队列完全清空:
[program|------------------unused--------------------------]
然后,系统能够缩小虚拟地址空间:
[program]
这会带您回到起点。
解决 方案
如果要"修复"此问题,一种选择是"反向"分配内存,即将最后分配的项目放在队列的前面。
另一种选择是通过mmap
为队列分配元素,例如Linux会自动为"大"分配执行此操作。 您可以通过使用 M_MMAP_THRESHOLD
调用 mallopt(3)
并将其设置为比结构大小小一点来更改此阈值。 这使得分配彼此独立,因此操作系统可以单独回收它们。 这种技术甚至可以应用于现有程序而无需重新编译,因此如果您需要在无法修改的程序中解决此问题,则通常很有用。
C++实现会调用一些operator delete
来释放动态分配的(使用一些operator new
(内存。在几个C++标准库中,new
调用malloc
,delete
调用free
。
(我专注于Linux的观点,但原理在其他操作系统上是相似的(
但是,虽然malloc
(或::operator new
(有时会通过系统调用来要求操作系统内核增加内存,如mmap(2(,free
(或::operator delete
(通常只是简单地将释放的内存区域标记为重新可用于将来对malloc
(或new
(的调用。
所以从内核的角度来看 (例如/proc/
, 参见 proc(5(...( 虚拟地址空间没有改变, 内存仍然被消耗, 即使在应用程序内部它被标记为 "freed" 并且将在将来的某个分配中重用 (通过将来对 malloc
或 new
的调用(
大多数标准容器C++内部都使用堆数据。特别是您的本地(堆栈分配(std::map
或std::vector
(或std::deque
(变量将调用内部数据的new
和delete
。
顺便说一句,我觉得你的声明很奇怪。除非每个struct pxData
都有 65536 个已用valueData
插槽,否则我建议使用一些 std::vector,所以有
std::vector<std::complex<float>> valueData;
并相应地改进您的代码。您可能需要做一些valueData.reserve(somesize);
和/或valueData.resize(somesize);
和/或valueData.push_back(somecomplexnumber);
等。
- 如何在c++中释放内存
- 为什么这个 std::queue/指向结构的指针列表直到 List.Size() == 0 才释放内存?
- std::unordered_map析构函数不释放内存?
- 在C++中释放内存期间,迭代器与指针有何不同
- 使用共享指针时,从共享指针本身释放内存的机制是什么
- 释放内存(主题模板)时出现问题
- 使用后自动释放内存
- C++ 如何释放内存
- 从函数内对象的向量中释放内存
- C++ 在不释放内存的情况下调用析构函数
- 多个线程之间的获取-释放内存顺序
- C++ - 析构函数只是释放内存还是实际删除对象
- 使用 RAII 替换最终块以释放内存
- 如何通过带有指向基类的指针的删除运算符释放内存
- 代码中的"sprintf"用法是否需要释放内存?
- C++何时使用 delete[] 并正确释放内存?
- 为什么此获取和释放内存围栏不能给出一致的值?
- 抛出新表达式的参数子表达式时释放内存
- 在这种情况下,如何释放内存?
- 在 c++ 中应按什么顺序释放内存?