malloc实现是否会将free-ed内存返回给系统?

时间:2010-02-06 23:58:12

标签: free malloc glibc mmap tcmalloc

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

在这方面,是什么行为:

  • ptmalloc 1,2(默认为glibc)或3
  • dlmalloc
  • tcmalloc(google threaded malloc)
  • solaris 10-11默认malloc和mtmalloc
  • FreeBSD 8默认malloc(jemalloc)
  • Hoard malloc?

更新

如果我有一个应用程序,其白天和夜晚的内存消耗可能会大不相同(例如),我可以强制任何malloc将系统释放的内存吗?

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

8 个答案:

答案 0 :(得分:30)

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

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

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

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

    然而,使用M_TRIM_THRESHOLD运行的初步测试程序表明,即使malloc分配的内存确实返回系统,最初通过{{1}请求的实际内存块(竞技场)的剩余部分也是如此。倾向于保留。

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

    使用pad的{​​{1}}的这种修改行为的实现可以使用malloc钩子功能来完成。这不需要对核心glibc库进行任何源代码更改。

  3. 在glibc的免费实现中使用free()系统调用。

答案 1 :(得分:15)

大多数实现都不打算识别那些(相对罕见的)整个“块”(适合操作系统的大小)已被释放并可以返回的情况,但当然有例外。例如,我引用了the wikipedia page,在OpenBSD中:

  

在致电free时,会释放内存   并从进程地址取消映射   空间使用munmap。这个系统是   旨在通过采取改善安全性   地址空间布局的优点   随机化和差距页面功能   作为OpenBSD mmap的一部分实现的   系统调用,并进行检测   免费使用后的bug - 作为一个大内存   分配完全取消映射   在它被释放后,进一步使用会导致   分段故障和终止   该计划。

但是,大多数系统并不像OpenBSD那样以安全为中心。

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

答案 2 :(得分:5)

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

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

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    the documentation提及

      

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

    但是10对我来说似乎不够(因为我看不到任何变化)。

  2. 在代码中的某处找到释放所有释放内存的有趣内容,然后添加以下代码:

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    
  3. 第二种解决方案在我的案例中非常有效;第一个会很棒,但不是很成功,例如找到正确的数字很复杂。

答案 3 :(得分:4)

对于所有'普通'malloc,包括你提到的那些,内存被释放以供你的进程重用,但不会回到整个系统。只有在最终终止处理时才会释放回整个系统。

答案 4 :(得分:4)

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

答案 5 :(得分:4)

我在我的应用程序中遇到了类似的问题,经过一些调查我发现由于某种原因,当分配的对象很小时(在我的情况下小于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

答案 6 :(得分:2)

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

答案 7 :(得分:0)

FreeBSD 12的malloc(3)使用jemalloc 5.1,它使用madvise(...MADV_FREE)将释放的内存(“脏页”)返回给OS。

仅在opt.dirty_decay_msopt.muzzy_decay_ms控制的时间延迟之后才返回释放的内存;有关更多详细信息,请参见manual page和此issue on implementing decay-based unused dirty page purging

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