我一直在嵌入式Linux系统上运行隔夜内存测试。使用vmstat我发现随着时间的推移,空闲内存会逐渐减少。根据 procfs 中的一些 smaps 分析,一个进程的堆以大致相同的速率增长。我怀疑内存泄漏,并在代码中发现了一些经常使用new
和delete
的位置。但是,我没有看到new
次来电,而没有匹配delete
来电。
我再次运行内存测试,今天早上通过以下调用清除了内存缓存
echo 3 > /proc/sys/vm/drop_caches
vmstat中列出的空闲内存下降到接近测试开始时的值。
内核是否定期回收未使用的堆页面?如果是这样,除了上面的那个之外还有其他时间吗?可能是当空闲内存低于某个阈值时?
答案 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一次,然后在用户空间中循环摆动。