我有一个长期存在的应用程序,经常进行内存分配和释放。任何 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.
发布评论
评论(8)
以下分析仅适用于glibc(基于
ptmalloc2
算法)。有一些选项似乎有助于将释放的内存返回到系统:
mallopt()(在
malloc.h
中定义)确实提供了一个选项,可使用参数选项之一M_TRIM_THRESHOLD
来设置修剪阈值code>,这表示数据段顶部允许的最小可用内存量(以字节为单位)。如果数量低于此阈值,glibc 会调用brk()
将内存返还给内核。Linux中
M_TRIM_THRESHOLD
的默认值设置为128K,设置较小的值可能会节省空间。通过在环境变量
MALLOC_TRIM_THRESHOLD_
中设置修剪阈值可以实现相同的行为,绝对不需要更改源代码。但是,使用
M_TRIM_THRESHOLD
运行的初步测试程序表明,即使malloc
分配的内存确实返回到系统,实际内存块的剩余部分最初通过brk()
请求的(竞技场)往往会被保留。可以通过调用
malloc_trim(pad)
(在malloc.h
中定义)来修剪内存区域并将任何未使用的内存返回给系统。此函数调整数据段的大小,在其末尾至少留下pad
字节,如果可以释放的字节少于一页,则失败。段大小始终是一页的倍数,在 i386 上为 4,096 字节。可以使用 malloc 挂钩功能来实现使用
malloc_trim
的free()
的修改行为。这不需要对核心 glibc 库进行任何源代码更改。在
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:
mallopt() (defined in
malloc.h
) does provide an option to set the trim threshold value using one of the parameter optionM_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 invokesbrk()
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 bymalloc
does return to the system, the remaining portion of the actual chunk of memory (the arena) initially requested viabrk()
tends to be retained.It is possible to trim the memory arena and give any unused memory back to the system by calling
malloc_trim(pad)
(defined inmalloc.h
). This function resizes the data segment, leaving at leastpad
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()
usingmalloc_trim
could be done using the malloc hook functionality. This would not require any source code changes to the core glibc library.Using
madvise()
system call inside the free implementation ofglibc
.大多数实现不会费心识别那些(相对罕见)整个“块”(无论大小适合操作系统)已被释放并可以返回的情况,但当然也有例外。例如,我引用 OpenBSD 中的维基百科页面:
不过,大多数系统并不像 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:
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).我在我的应用程序中遇到了类似的问题,经过一番调查后,我注意到由于某种原因,当分配的对象很小(在我的例子中小于 120 字节)时,glibc 不会将内存返回给系统。
看这段代码:
程序输出:
约64MB未返回系统。当我将 typedef 更改为:
typedef x<110>; X;
程序输出如下所示:几乎所有内存都被释放。我还注意到,在任何一种情况下使用
malloc_trim(0)
都会向系统释放内存。以下是将
malloc_trim
添加到上面的代码后的输出: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:
Program output:
about 64 MB are not return to system. When I changed typedef to:
typedef x<110> X;
program output looks like this: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:我正在处理与OP相同的问题。到目前为止,tcmalloc 似乎是可行的。我找到了两个解决方案:
使用链接的 tcmalloc 编译程序,然后将其启动为:
文档提到了这一点
<块引用>
合理的费率在 [0,10] 范围内。
但是 10 对我来说似乎还不够(即我看不到任何变化)。
在代码中找到需要释放所有已释放内存的位置,然后添加以下代码:
第二种解决方案在我的情况下非常有效;第一个会很棒,但不是很成功,例如找到正确的数字很复杂。
I am dealing with the same problem as the OP. So far, it seems possible with tcmalloc. I found two solutions:
compile your program with tcmalloc linked, then launch it as :
the documentation mentions that
but 10 doesn't seem enough for me (i.e I see no change).
find somewhere in your code where it would be interesting to release all the freed memory, and then add this code:
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.
在您列出的那些中,只有 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.
简短的回答:要强制
malloc
子系统将内存返回给操作系统,请使用malloc_trim()
。否则,返回内存的行为取决于实现。The short answer: To force
malloc
subsystem to return memory to OS, usemalloc_trim()
. Otherwise, behavior of returning memory is implementation dependent.对于所有“正常”malloc,包括您提到的那些,内存都会被释放以供您的进程重用,但不会返回到整个系统。仅当进程最终终止时才会释放回整个系统。
For all 'normal'
malloc
s, 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.FreeBSD 12 的
malloc(3)
使用 jemalloc 5.1,它返回释放的内存(“脏页”) ") 使用madvise(...MADV_FREE)
连接到操作系统。释放的内存仅在
opt.dirty_decay_ms
和opt.muzzy_decay_ms
控制的时间延迟后返回;请参阅手册页和此有关实现基于衰减的未使用脏页清除的问题了解更多详细信息。早期版本的 FreeBSD 附带了旧版本的 jemalloc,它也返回释放的内存,但使用不同的算法来决定清除什么以及何时清除。
FreeBSD 12's
malloc(3)
uses jemalloc 5.1, which returns freed memory ("dirty pages") to the OS usingmadvise(...MADV_FREE)
.Freed memory is only returned after a time delay controlled by
opt.dirty_decay_ms
andopt.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.