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

时间:2015-02-19 17:05:44

标签: linux memory operating-system malloc glibc

我对在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)将内存释放回操作系统

所以我想知道man-page是错误的还是我错误解释了malloc / malloc.c中的源代码。

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

2 个答案:

答案 0 :(得分:2)

  

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

madvise(x, MADV_DONTNEED)没有发布内存。 man madvise

  

<强> MADV_DONTNEED
                不要指望在不久的将来访问。 (暂且,                 应用程序完成了给定的范围,所以内核                 可以释放与之相关的资源。)后续访问                 此范围内的页面将成功,但将导致                 从底层映射文件重新加载内存内容                 (请参阅 mmap (2))或零填充按需页面,以便进行映射                 基础文件。

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

  

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

答案 1 :(得分:0)

现在glibc中madviseMADV_DONTNEED有两种用法:http://code.metager.de/source/search?q=MADV_DONTNEED&path=%2Fgnu%2Fglibc%2Fmalloc%2F&project=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);

2007年12月16日,Ulrich Drepper提出了https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=68631c8eb92ff38d9da1ae34f6aa048539b199cc提案(glibc 2.9及更新版本的一部分):

  
      
  • malloc / malloc.c(public_mTRIm):遍历所有竞技场并致电
  •   
     

所有人的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.3,并且是2012年由kerrisk提交的:https://github.com/mkerrisk/man-pages/commit/a15b0e60b297e29c825b7417582a33e6ca26bf65

我可以grep the glibc's git, there are no man pages in the glibc,并且没有提交malloc_trim手册页来记录这个补丁。 glibc malloc的最佳和唯一文档是其源代码:https://sourceware.org/git/?p=glibc.git;a=blob;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中的文本(并且在2007年没有更新{和malloc_trim中的描述)并且没有在man-pages项目中记录。 2012年的手册页可能是该函数的第一个手册页,不是由glibc的作者编写的。 glibc的信息页面仅提到128 KB的M_TRIM_THRESHOLD: https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html#Malloc-Tunable-Parameters并且不要列出malloc_trim函数https://www.gnu.org/software/libc/manual/html_node/Summary-of-Malloc.html#Summary-of-Malloc(并且它也不会记录memusage / memusagestat / libmemusage.so)。

您可能会再次询问Drepper和其他glibc开发人员,就像您在https://sourceware.org/ml/libc-help/2015-02/msg00022.html&#34; malloc_trim()行为&#34;中所做的那样,但仍然没有他们的回复。 (只有来自其他用户的错误答案,例如https://sourceware.org/ml/libc-help/2015-05/msg00007.html https://sourceware.org/ml/libc-help/2015-05/msg00008.html

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

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <malloc.h>

int main()
{
    int *m1,*m2,*m3,*m4;
    printf("%s\n","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:%p\n", 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 started\n", 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

因此,madvise调用后有9个页面有MADV_DONTNEED malloc_trim(0),当堆中间有40008个字节的洞时。