可以malloc_trim()从堆的中间释放内存



我对glibc中实现的malloc_trim的行为感到困惑。

man malloc_trim
[...]
malloc_trim - release free memory from the top of the heap
[...]
This function cannot release free memory located at places other than the top of the heap.

当我现在查找malloc_trim()的源(在malloc/malloc.c中)时,我发现它调用mtrim(),后者正在利用madvise(x, MADV_DONTNEED)将内存释放回操作系统。

所以我想知道手册页是否是错误的,或者我是否误解了malloc/malloc.c.中的来源

malloc_trim()能否从堆的中间释放内存?

在glibc中,madviseMADV_DONTNEED现在有两种用法:http://code.metager.de/source/search?q=MADV_DONTNEED&路径=%2Fgnu%2Fglibc%2Fmalloc%2F&项目=gnu

 H A D  arena.c 643 __madvise ((char *) h + new_size, diff, MADV_DONTNEED);
 H A D  malloc.c    4535 __madvise (paligned_mem, size & ~psm1, MADV_DONTNEED);

有https://sourceware.org/git/?p=glibc.git;a=提交;f=malloc/malloc.c;h=68631c8eb92ff38d9da1ae34f6a048539b199cc由Ulrich Drepper于2007年12月16日提交(glibc 2.9及更新版本的一部分):

  • malloc/malloc.c(public_mTRIm):遍历所有arena并调用

mTRIm。(mTRIm):额外迭代所有空闲块并使用madvise为包含至少一个的所有块释放内存内存页。

mTRIm(现在的mtrim)的实现方式发生了变化。块的未使用部分,根据页面大小对齐,并且大小大于页面,可以标记为MADV_DONTNEED:

           /* See whether the chunk contains at least one unused page.  */
           char *paligned_mem = (char *) (((uintptr_t) p
                                           + sizeof (struct malloc_chunk)
                                           + psm1) & ~psm1);
           assert ((char *) chunk2mem (p) + 4 * SIZE_SZ <= paligned_mem);
           assert ((char *) p + size > paligned_mem);
           /* This is the size we could potentially free.  */
           size -= paligned_mem - (char *) p;
           if (size > psm1)
               madvise (paligned_mem, size & ~psm1, MADV_DONTNEED);

malloc_trim的主页如下:https://github.com/mkerrisk/man-pages/blob/master/man3/malloc_trim.3kerrisk于2012年承诺:https://github.com/mkerrisk/man-pages/commit/a15b0e60b297e29c825b7417582a33e6ca26bf65

由于我可以grep glibc的git,所以glibc中没有手册页,也没有提交malloc_trim手册页来记录这个补丁。glibcmalloc最好也是唯一的文档是它的源代码:https://sourceware.org/git/?p=glibc.git;a=斑点;f=malloc/malloc.c

Additional functions:
 malloc_trim(size_t pad);
 609 /*
 610   malloc_trim(size_t pad);
 611 
 612   If possible, gives memory back to the system (via negative
 613   arguments to sbrk) if there is unused memory at the `high' end of
 614   the malloc pool. You can call this after freeing large blocks of
 615   memory to potentially reduce the system-level memory requirements
 616   of a program. However, it cannot guarantee to reduce memory. Under
 617   some allocation patterns, some large free blocks of memory will be
 618   locked between two used chunks, so they cannot be given back to
 619   the system.
 620 
 621   The `pad' argument to malloc_trim represents the amount of free
 622   trailing space to leave untrimmed. If this argument is zero,
 623   only the minimum amount of memory to maintain internal data
 624   structures will be left (one page or less). Non-zero arguments
 625   can be supplied to maintain enough trailing space to service
 626   future expected allocations without having to re-obtain memory
 627   from the system.
 628 
 629   Malloc_trim returns 1 if it actually released any memory, else 0.
 630   On systems that do not support "negative sbrks", it will always
 631   return 0.
 632 */
 633 int      __malloc_trim(size_t);
 634 

在malloc/malloc.c中,从块的中间释放并没有作为文本进行记录(而commit中的malloc_trim描述在2007年没有更新),也没有在手册页项目中进行记录。2012年的手册页可能是该功能的第一个手册页,不是由glibc的作者编写的。glibc的信息页面只提到128 KB的M_TRIM_THRESHOLD:https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html#Malloc-可调参数和不列出malloc_trim函数https://www.gnu.org/software/libc/manual/html_node/Summary-of-Malloc.html#Summary-它也没有记录memusagestat/libmemousagest.so)

你可以再次询问Drepper和其他能说会道的开发人员,就像你在https://sourceware.org/ml/libc-help/2015-02/msg00022.html"malloc_trim()behavior",但他们仍然没有回复。(只有来自其他用户的错误答案,如https://sourceware.org/ml/libc-help/2015-05/msg00007.htmlhttps://sourceware.org/ml/libc-help/2015-05/msg00008.html)

或者你可以用这个简单的C程序(test_malloc_trim.c)和strace/ltrace:测试malloc_trim

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <malloc.h>
int main()
{
    int *m1,*m2,*m3,*m4;
    printf("%sn","Test started");
    m1=(int*)malloc(20000);
    m2=(int*)malloc(40000);
    m3=(int*)malloc(80000);
    m4=(int*)malloc(10000);
    printf("1:%p 2:%p 3:%p 4:%pn", m1, m2, m3, m4);
    free(m2);
    malloc_trim(0); // 20000, 2000000
    sleep(1);
    free(m1);
    free(m3);
    free(m4);
    // malloc_stats(); malloc_info(0, stdout);
    return 0;
}

gcc test_malloc_trim.c -o test_malloc_trimstrace ./test_malloc_trim

write(1, "Test startedn", 13Test started
)          = 13
brk(0)                                  = 0xcca000
brk(0xcef000)                           = 0xcef000
write(1, "1:0xcca010 2:0xccee40 3:0xcd8a90"..., 441:0xcca010 2:0xccee40 3:0xcd8a90 4:0xcec320
) = 44
madvise(0xccf000, 36864, MADV_DONTNEED) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7ffffafbfff0)       = 0
brk(0xceb000)                           = 0xceb000

因此,在malloc_trim(0)调用之后,当堆的中间有40008字节的洞时,9页的madviseMADV_DONTNEED

利用madvise(x,MADV_DONTNEED)将内存释放回操作系统。

madvise(x, MADV_DONTNEED)不会释放内存。man madvise:

MADV_DONTNED
不要指望在不久的将来访问。(就目前而言,应用程序在给定的范围内完成,因此内核可以释放与其关联的资源。)的后续访问此范围内的页面将成功,但会导致从底层映射文件重新加载内存内容(请参见mmap(2))或零填充按需页面,以获取没有底层文件。

因此,madvise(x, MADV_DONTNEED)的使用与man malloc_trim的声明并不矛盾:

此函数无法释放位于堆顶部以外位置的可用内存。

相关内容

  • 没有找到相关文章

最新更新