64

我有一个长期存在的应用程序,经常进行内存分配释放。任何malloc实现都会将释放的内存返回给系统吗?

在这方面,以下行为是什么:

  • ptmalloc 1、2(glibc 默认)或 3
  • dlmalloc
  • tcmalloc(谷歌线程 malloc)
  • solaris 10-11 默认 malloc 和 mtmalloc
  • FreeBSD 8 默认 malloc (jemalloc)
  • 囤积malloc?

更新

如果我有一个应用程序的内存消耗在白天和夜间(例如)可能非常不同,我可以强制任何一个malloc将释放的内存返回给系统吗?

如果没有这样的返回,释放的内存将被多次换出,但这样的内存只包含垃圾。

4

8 回答 8

39

以下分析仅适用于 glibc(基于ptmalloc2算法)。某些选项似乎有助于将释放的内存返回给系统:

  1. mallopt()(定义在 中malloc.h)确实提供了一个使用参数 option 之一设置修剪阈值的选项M_TRIM_THRESHOLD,这表示数据段顶部允许的最小可用内存量(以字节为单位)。如果数量低于此阈值,glibc 调用brk()以将内存归还给内核。

    Linux 中的默认值M_TRIM_THRESHOLD设置为 128K,设置较小的值可能会节省空间。

    通过在环境变量中设置修剪阈值可以实现相同的行为MALLOC_TRIM_THRESHOLD_,绝对不会更改源。

    但是,使用运行的初步测试程序M_TRIM_THRESHOLD表明,即使分配的内存malloc确实返回给系统,最初请求的实际内存块(竞技场)的剩余部分brk()往往会保留下来。

  2. malloc_trim(pad)可以通过调用(在 中定义)来修剪内存区域并将任何未使用的内存归还给系统malloc.h。此函数调整数据段的大小,在其末尾至少保留pad字节,如果可以释放少于一页的字节,则失败。段大小始终是一页的倍数,在 i386 上为 4,096 字节。

    可以使用 malloc 挂钩功能来实现这种修改后的free()using行为。malloc_trim这不需要对核心 glibc 库进行任何源代码更改。

  3. madvise()在自由实现里面使用系统调用glibc

于 2010-10-05T02:35:58.277 回答
17

大多数实现不会费心识别那些(相对罕见的)整个“块”(无论大小适合操作系统)已被释放并可以返回的情况,但当然也有例外。例如,我在 OpenBSD 中引用了维基百科页面

在调用 时free,使用 munmap 从进程地址空间释放和取消映射内存。该系统旨在通过利用作为 OpenBSDmmap 系统调用的一部分实现的地址空间布局随机化和间隙页面特性来提高安全性,并检测释放后使用错误——因为大内存分配在释放后完全未映射,进一步使用会导致分段错误和程序终止。

不过,大多数系统不像 OpenBSD 那样注重安全。

知道了这一点,当我编写一个长时间运行的系统时,该系统对大量内存具有已知的暂时性需求,我总是尝试fork这个过程:然后父级只是等待来自子级的结果 [[通常在管道上]],子进程进行计算(包括内存分配),返回结果 [[在所述管道上]],然后终止。这样,我的长时间运行的进程就不会在其内存需求的偶尔“峰值”之间长时间无用地占用内存。其他替代策略包括针对此类特殊要求切换到自定义内存分配器(C++ 使其相当容易,尽管 Java 和 Python 等带有虚拟机的语言通常不会这样做)。

于 2010-02-07T00:26:20.357 回答
7

我在我的应用程序中遇到了类似的问题,经过一番调查,我注意到由于某种原因,当分配的对象很小(在我的情况下小于 120 字节)时,glibc 不会将内存返回给系统。
看看这段代码:

#include <list>
#include <malloc.h>

template<size_t s> class x{char x[s];};

int main(int argc,char** argv){
    typedef x<100> X;

    std::list<X> lx;
    for(size_t i = 0; i < 500000;++i){
        lx.push_back(X());
    }

    lx.clear();
    malloc_stats();

    return 0;
}

程序输出:

Arena 0:
system bytes     =   64069632
in use bytes     =          0
Total (incl. mmap):
system bytes     =   64069632
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

大约 64 MB 不会返回系统。当我将 typedef 更改为: typedef x<110> X;程序输出如下所示:

Arena 0:
system bytes     =     135168
in use bytes     =          0
Total (incl. mmap):
system bytes     =     135168
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

几乎所有的内存都被释放了。我还注意到,malloc_trim(0)在任何一种情况下都使用释放内存到系统。
这是添加malloc_trim到上面的代码后的输出:

Arena 0:
system bytes     =       4096
in use bytes     =          0
Total (incl. mmap):
system bytes     =       4096
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0
于 2013-12-15T18:44:59.670 回答
6

我正在处理与 OP 相同的问题。到目前为止,tcmalloc 似乎是可能的。我找到了两个解决方案:

  1. 使用链接的 tcmalloc 编译您的程序,然后将其启动为:

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    文档提到

    合理的费率在 [0,10] 范围内。

    但 10 对我来说似乎还不够(即我看不到任何变化)。

  2. 在代码中找到释放所有释放内存会很有趣的地方,然后添加以下代码:

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    

就我而言,第二种解决方案非常有效;第一个会很好,但不是很成功,例如,找到正确的数字很复杂。

于 2010-03-10T15:10:12.297 回答
4

在您列出的那些中,只有 Hoard 会将内存返回给系统......但如果它真的可以做到,这将在很大程度上取决于您的程序的分配行为。

于 2010-02-07T00:27:18.440 回答
3

对于所有“正常” malloc,包括您提到的那些,内存被释放以供您的进程重用,但不会返回到整个系统。仅当您的进程最终终止时才会释放回整个系统。

于 2010-02-07T00:02:02.927 回答
3

简短的回答:要强制malloc子系统将内存返回给操作系统,请使用malloc_trim(). 否则,返回内存的行为取决于实现。

于 2013-05-23T18:25:19.320 回答
1

FreeBSD 12malloc(3)使用jemalloc 5.1,它使用madvise(...MADV_FREE).

释放的内存仅在由opt.dirty_decay_msand控制的时间延迟后返回opt.muzzy_decay_ms;有关更多详细信息,请参阅有关实施基于衰减的未使用脏页清除的手册页和此问题。

较早版本的 FreeBSD 附带较旧版本的 jemalloc,它也返回释放的内存,但使用不同的算法来决定清除什么以及何时清除。

于 2019-07-18T16:53:02.413 回答