强制free()将malloc分配的内存归还给操作系统

11

似乎即使我使用malloc()为Linux进程分配的所有内存都已经被释放,该进程仍然保留了内存而没有将其返回给操作系统。

运行默认情况下没有泄漏的valgrind massif工具。

使用--pages-as-heap=yes选项运行valgrind会显示如下内容:

->13.77% (7,655,424B) 0x35FEEEB069: brk (brk.c:31)

->13.77% (7,655,424B) 0x35FEEEB113: sbrk (sbrk.c:53)

->13.77% (7,655,424B) 0x35FEE82717: __default_morecore (morecore.c:48)

->13.77% (7,655,424B) 0x35FEE7DCCB: _int_malloc (malloc.c:2455)

->13.77% (7,655,424B) 0x35FEE7F4F1: malloc (malloc.c:2862)

因此,尽管内存已经通过free()释放,但看起来malloc调用了brk/sbrk并未将其返回给操作系统。

我该如何强制free()立即调用sbrk()并将所有内存都返回给操作系统?

我正在运行一个非常低端的平台,每个MB都很宝贵。

提前感谢您的帮助。


malloc - 内存分配。它只分配内存,不会“释放”可重复使用的内存,这就是为什么有free()的原因。 - Marc B
@JoachimPileborg,看起来这个好像不行。 我从Linux内核中收到了崩溃报告,说内存不足。 当我使用fork调用我的进程时,它被杀死,sbrk被释放,然后我才能继续。 所以操作系统没有调用sbrk来释放之前释放的内存。 - Itay Marom
@JoachimPileborg 点击这里 http://man7.org/linux/man-pages/man3/mallopt.3.html M_TRIM_THRESHOLD 这回答了问题 - malloc等待足够的连续空闲内存来调用sbrk并将内存归还给操作系统。 - Itay Marom
这个问题的标题有些令人困惑,让我以为提问者不知道 free() 函数。 - Iharob Al Asimi
1
你应该看一下这个网址:https://dev59.com/w3E95IYBdhLWcg3wp_qn - Jean-Baptiste Yunès
显示剩余3条评论
2个回答

6
使用 glibc malloc 尝试调用 malloc_trim 函数。这个函数文档不是很好,在2007年(glibc 2.9)左右有一些变化 - 参考 https://dev59.com/LZzha4cB1Zd3GeqPD1sM#42281428
自2007年以来,该函数将会:迭代所有 malloc 内存区域(在多线程应用程序中使用)进行修剪和快速bin整理;并释放所有已完全释放的对齐(4KB)页面。
参考 https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=68631c8eb92ff38d9da1ae34f6aa048539b199cc

Ulrich Drepper Sun, 16 Dec 2007 22:53:08 +0000 (22:53 +0000)

  • malloc/malloc.c (public_mTRIm): 迭代所有内存池,并为它们中的每一个调用 mTRIm。

(mTRIm):此外,还要迭代所有空闲块,并使用 madvise 释放至少包含一页内存的所有块的内存。

参考 https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=c54c203cbf1f024e72493546221305b4fd5729b7;hp=1e716089a2b976d120c304ad75dd95c63737ad75;hb=68631c8eb92ff38d9da1ae34f6aa048539b199cc;hpb=52386be756e113f20502f181d780aecc38cbb66a
+  malloc_consolidate (av);
...
+  for (int i = 1; i < NBINS; ++i)
...
+        for (mchunkptr p = last (bin); p != bin; p = p->bk)
+         {
...
+               /* See whether the chunk contains at least one unused page.  */
+               char *paligned_mem = (char *) (((uintptr_t) p
+                                               + sizeof (struct malloc_chunk)
+                                               + psm1) & ~psm1);
...
+               /* This is the size we could potentially free.  */
+               size -= paligned_mem - (char *) p;
+
+               if (size > psm1)
+                 {
...
+                   madvise (paligned_mem, size & ~psm1, MADV_DONTNEED);

因此,调用malloc_trim会将几乎所有已释放的内存返回给操作系统。只有包含仍未被释放数据的页面将被保留;使用MADV_DONTNEED参数时,操作系统可以取消映射或不取消映射物理页面,而linux通常会取消映射。madvise的页面仍计入VSIZE(进程的总虚拟内存大小),但通常有助于减少RSS(进程使用的物理内存量)。

或者,您可以尝试切换到替代的内存分配库:tcmalloc(gperftools / google-perftools)或jemalloc(Facebook),它们都采用了激进的规则来将已释放的内存返回给操作系统(使用madvise MADV_DONTNEED甚至是MADV_FREE)。


6

唯一可靠且可移植的方法是通过退出进程并重新启动进程,恢复任何需要继续的状态,来使操作系统回收内存。

当然,根据您的需求使用brk/sbrk编写自己的malloc/free实现是另一种选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接