如果/何时取消分配的堆内存被回收?

时间:2012-08-29 13:20:56

标签: c++ linux memory-management memory-leaks heap-memory

我一直在嵌入式Linux系统上运行隔夜内存测试。使用vmstat我发现随着时间的推移,空闲内存会逐渐减少。根据 procfs 中的一些 smaps 分析,一个进程的堆以大致相同的速率增长。我怀疑内存泄漏,并在代码中发现了一些经常使用newdelete的位置。但是,我没有看到new次来电,而没有匹配delete来电。

我再次运行内存测试,今天早上通过以下调用清除了内存缓存

echo 3 > /proc/sys/vm/drop_caches

vmstat中列出的空闲内存下降到接近测试开始时的值。

内核是否定期回收未使用的堆页面?如果是这样,除了上面的那个之外还有其他时间吗?可能是当空闲内存低于某个阈值时?

4 个答案:

答案 0 :(得分:4)

正如其他人所说,将内存返回内核是进程的责任。

通常有两种方式来分配内存:如果你malloc() / new内存块超过一定大小,内存将通过mmap()从操作系统分配并尽快进行转换因为它是免费的。通过向上移动sbrk边界来增加进程的数据区域来分配较小的块。只有在该段结尾处超过特定大小的块空闲时,才释放该存储器。

例如:(伪代码,我不太了解C ++)

a = new char[1000];
b = new char[1000];

记忆图:

---------------+---+---+
end of program | a | b |
---------------+---+---+

如果你现在释放a,你的中间会有一个洞。它没有被释放,因为它无法被释放。 如果您释放b,则进程的内存可能会减少,也可能不会减少;未使用的余数将返回给系统。

使用像

这样简单的程序进行测试
#include <stdlib.h>

int main()
{
    char * a = malloc(100000);
    char * b = malloc(100000);
    char * c = malloc(100000);
    free(c);
    free(b);
    free(a);
}

导致strace输出,如

brk(0)                                  = 0x804b000
brk(0x8084000)                          = 0x8084000
brk(0x80b5000)                          = 0x80b5000
brk(0x809c000)                          = 0x809c000
brk(0x8084000)                          = 0x8084000
brk(0x806c000)                          = 0x806c000

表示brk值首先增加(malloc()),然后再次减少(free())。

答案 1 :(得分:1)

内核将在需要时回收缓存的内存页,即当系统内存不足时。进程的内存管理器(在这种情况下是C ++库中的new / delete实现是否由进程的内存管理器自行决定是否将进程堆(自由存储)中的内存页返回给操作系统。这是一个完全自愿的操作,内核无需这样做。

drop_caches完成这个技巧的事实来看,你可以推断出内存缓存,而不是进程的堆,它正在填补内存。使用free命令查明实际可用于应用程序的内存量,尤其是它报告的-/+ buffers/cache行。

答案 2 :(得分:1)

在程序中调用delete会导致内存返回到程序运行时的内存管理器。原则上,这可以写入以便将释放的内存返回给操作系统,但如果确实如此,我会感到惊讶。而是将回收的内存放在一边,以便随后调用new

请注意,这是您的流程的虚拟内存;在程序执行期间,它实际驻留在物理内存中的程度取决于整体系统负载,并由操作系统处理。

答案 3 :(得分:0)

用户对malloc和free(或new和delete)的调用,据我所知,永远不会将不再使用的页面返回给操作系统。相反,他们只记得已经释放了什么内存,这样如果你做一个可以通过以前释放的内存满足的大小的malloc / new,那么它将使用它,而不是去O / S并使用sbrk来获取更多的记忆。

因此这段代码:

for (;;)
{
    struct { char data[200 * 1024 * 1024] } HugeBuffer;
    HugeBuffer *buff = new HugeBuffer;
    delete buff;
}

将分配200Mb一次,然后永远稳定地使用该内存。它将在原始分配上转到O / S一次,然后在用户空间中循环摆动。