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

osg*_*sgx 54 malloc free mmap glibc tcmalloc

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

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

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

更新

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

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

小智 32

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

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

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

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

    但是,使用的初步测试程序M_TRIM_THRESHOLD已经表明,即使malloc分配的内存确实返回系统,最初请求的实际内存块(竞技场)的剩余部分brk()也会被保留.

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

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

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

  • 还要检查`M_MMAP_THRESHOLD`,这是一个阈值,高于该阈值`malloc()`使用`mmap()`来获取内存.显然,如果您释放超过此大小的块,则将内存返回给操作系统. (4认同)

Ale*_*lli 15

大多数实现都不打算识别那些(相对罕见的)整个"块"(适合操作系统的大小)已被释放并可以返回的情况,但当然有例外.例如,我在维基百科页面中引用OpenBSD:

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

但是,大多数系统并不像OpenBSD那样以安全为重点.

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

  • @osgx:是的,只要您只使用它来执行新进程,就可以在多线程应用程序中进行分叉.好吧,实际上"......子进程可能只执行异步信号安全操作,直到调用其中一个exec函数为止" (2认同)

Lau*_*con 5

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

  1. 编译你的程序与tcmalloc链接,然后启动它:

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    
    Run Code Online (Sandbox Code Playgroud)

    文件中提到了这一点

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

    但是10对我来说似乎不够(也就是说我看不到变化).

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

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    
    Run Code Online (Sandbox Code Playgroud)

在我的情况下,第二种解决方案非常有效; 第一个会很棒,但不是很成功,例如找到正确的数字很复杂.


小智 5

在我的应用程序中,我遇到了类似的问题,经过一番调查,我注意到由于某些原因,当分配的对象很小(在我的情况下小于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;
}
Run Code Online (Sandbox Code Playgroud)

程序输出:

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
Run Code Online (Sandbox Code Playgroud)

大约有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
Run Code Online (Sandbox Code Playgroud)

几乎所有的内存都被释放。我还注意到,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
Run Code Online (Sandbox Code Playgroud)