malloc 实现会将释放的内存返回给系统吗?

发布于 2024-08-20 08:02:31 字数 439 浏览 8 评论 0 原文

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

在这方面,以下行为:

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

更新

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

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

I have a long-living application with frequent memory allocation-deallocation. Will any malloc implementation return freed memory back to the system?

What is, in this respect, the behavior of:

  • ptmalloc 1, 2 (glibc default) or 3
  • dlmalloc
  • tcmalloc (google threaded malloc)
  • solaris 10-11 default malloc and mtmalloc
  • FreeBSD 8 default malloc (jemalloc)
  • Hoard malloc?

Update

If I have an application whose memory consumption can be very different in daytime and nighttime (e.g.), can I force any of malloc's to return freed memory to the system?

Without such return freed memory will be swapped out and in many times, but such memory contains only garbage.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

栩栩如生 2024-08-27 08:02:31

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

  1. mallopt()(在 malloc.h 中定义)确实提供了一个选项,可使用参数选项之一M_TRIM_THRESHOLD来设置修剪阈值code>,这表示数据段顶部允许的最小可用内存量(以字节为单位)。如果数量低于此阈值,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 挂钩功能来实现使用 malloc_trimfree() 的修改行为。这不需要对核心 glibc 库进行任何源代码更改。

  3. glibc 的自由实现中使用 madvise() 系统调用。

The following analysis applies only to glibc (based on the ptmalloc2 algorithm).
There are certain options that seem helpful to return the freed memory back to the system:

  1. mallopt() (defined in malloc.h) does provide an option to set the trim threshold value using one of the parameter option M_TRIM_THRESHOLD, this indicates the minimum amount of free memory (in bytes) allowed at the top of the data segment. If the amount falls below this threshold, glibc invokes brk() to give back memory to the kernel.

    The default value of M_TRIM_THRESHOLD in Linux is set to 128K, setting a smaller value might save space.

    The same behavior could be achieved by setting trim threshold value in the environment variable MALLOC_TRIM_THRESHOLD_, with no source changes absolutely.

    However, preliminary test programs run using M_TRIM_THRESHOLD has shown that even though the memory allocated by malloc does return to the system, the remaining portion of the actual chunk of memory (the arena) initially requested via brk() tends to be retained.

  2. It is possible to trim the memory arena and give any unused memory back to the system by calling malloc_trim(pad) (defined in malloc.h). This function resizes the data segment, leaving at least pad bytes at the end of it and failing if less than one page worth of bytes can be freed. Segment size is always a multiple of one page, which is 4,096 bytes on i386.

    The implementation of this modified behavior of free() using malloc_trim could be done using the malloc hook functionality. This would not require any source code changes to the core glibc library.

  3. Using madvise() system call inside the free implementation of glibc.

终止放荡 2024-08-27 08:02:31

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

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

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

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

Most implementations don't bother identifying those (relatively rare) cases where entire "blocks" (of whatever size suits the OS) have been freed and could be returned, but there are of course exceptions. For example, and I quote from the wikipedia page, in OpenBSD:

On a call to free, memory is released
and unmapped from the process address
space using munmap. This system is
designed to improve security by taking
advantage of the address space layout
randomization and gap page features
implemented as part of OpenBSD's mmap
system call, and to detect
use-after-free bugs—as a large memory
allocation is completely unmapped
after it is freed, further use causes
a segmentation fault and termination
of the program.

Most systems are not as security-focused as OpenBSD, though.

Knowing this, when I'm coding a long-running system that has a known-to-be-transitory requirement for a large amount of memory, I always try to fork the process: the parent then just waits for results from the child [[typically on a pipe]], the child does the computation (including memory allocation), returns the results [[on said pipe]], then terminates. This way, my long-running process won't be uselessly hogging memory during the long times between occasional "spikes" in its demand for memory. Other alternative strategies include switching to a custom memory allocator for such special requirements (C++ makes it reasonably easy, though languages with virtual machines underneath such as Java and Python typically don't).

我不会写诗 2024-08-27 08:02:31

我在我的应用程序中遇到了类似的问题,经过一番调查后,我注意到由于某种原因,当分配的对象很小(在我的例子中小于 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

约64MB未返回系统。当我将 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

I had a similar problem in my app, after some investigation I noticed that for some reason glibc does not return memory to the system when allocated objects are small (in my case less than 120 bytes).
Look at this code:

#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;
}

Program output:

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

about 64 MB are not return to system. When I changed typedef to:
typedef x<110> X; program output looks like this:

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

almost all memory was freed. I also noticed that using malloc_trim(0) in either case released memory to system.
Here is output after adding malloc_trim to the code above:

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
平定天下 2024-08-27 08:02:31

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

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

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    文档提到了这一点

    <块引用>

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

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

  2. 在代码中找到需要释放所有已释放内存的位置,然后添加以下代码:

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

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

I am dealing with the same problem as the OP. So far, it seems possible with tcmalloc. I found two solutions:

  1. compile your program with tcmalloc linked, then launch it as :

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    the documentation mentions that

    Reasonable rates are in the range [0,10].

    but 10 doesn't seem enough for me (i.e I see no change).

  2. find somewhere in your code where it would be interesting to release all the freed memory, and then add this code:

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

The second solution has been very effective in my case; the first would be great but it isn't very successful, it is complicated to find the right number for example.

挽梦忆笙歌 2024-08-27 08:02:31

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

Of the ones you list, only Hoard will return memory to the system... but if it can actually do that will depend a lot on your program's allocation behaviour.

揽月 2024-08-27 08:02:31

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

The short answer: To force malloc subsystem to return memory to OS, use malloc_trim(). Otherwise, behavior of returning memory is implementation dependent.

你的笑 2024-08-27 08:02:31

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

For all 'normal' mallocs, including the ones you've mentioned, memory is released to be reused by your process, but not back to the whole system. Releasing back to the whole system happens only when you process is finally terminated.

慵挽 2024-08-27 08:02:31

FreeBSD 12 的 malloc(3) 使用 jemalloc 5.1,它返回释放的内存(“脏页”) ") 使用 madvise(...MADV_FREE) 连接到操作系统。

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

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

FreeBSD 12's malloc(3) uses jemalloc 5.1, which returns freed memory ("dirty pages") to the OS using madvise(...MADV_FREE).

Freed memory is only returned after a time delay controlled by opt.dirty_decay_ms and opt.muzzy_decay_ms; see the manual page and this issue on implementing decay-based unused dirty page purging for more details.

Earlier versions of FreeBSD shipped with older versions of jemalloc, which also returns freed memory, but uses a different algorithm to decide what to purge and when.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文