Can malloc_trim() release memory from the middle of the heap?

后端 未结 2 1612
傲寒
傲寒 2021-01-01 05:54

I am confused about the behaviour of malloc_trim as implemented in the glibc.

man malloc_trim
[...]
malloc_trim - release free memory from the top of the hea         


        
相关标签:
2条回答
  • 2021-01-01 06:18

    There are two usages of madvise with MADV_DONTNEED in glibc now: 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);
    

    There was https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=68631c8eb92ff38d9da1ae34f6aa048539b199cc commit by Ulrich Drepper on 16 Dec 2007 (part of glibc 2.9 and newer):

    • malloc/malloc.c (public_mTRIm): Iterate over all arenas and call

    mTRIm for all of them. (mTRIm): Additionally iterate over all free blocks and use madvise to free memory for all those blocks which contain at least one memory page.

    mTRIm (now mtrim) implementation was changed. Unused parts of chunks, aligned on page size and having size more than page may be marked as 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);
    

    Man page of malloc_trim is there: https://github.com/mkerrisk/man-pages/blob/master/man3/malloc_trim.3 and it was committed by kerrisk in 2012: https://github.com/mkerrisk/man-pages/commit/a15b0e60b297e29c825b7417582a33e6ca26bf65

    As I can grep the glibc's git, there are no man pages in the glibc, and no commit to malloc_trim manpage to document this patch. The best and the only documentation of glibc malloc is its source code: 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 
    

    Freeing from the middle of the chunk is not documented as text in malloc/malloc.c (and malloc_trim description in commend was not updated in 2007) and not documented in man-pages project. Man page from 2012 may be the first man page of the function, written not by authors of glibc. Info page of glibc only mentions M_TRIM_THRESHOLD of 128 KB: https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html#Malloc-Tunable-Parameters and don't list malloc_trim function https://www.gnu.org/software/libc/manual/html_node/Summary-of-Malloc.html#Summary-of-Malloc (and it also don't document memusage/memusagestat/libmemusage.so).

    You may ask Drepper and other glibc developers again as you already did in https://sourceware.org/ml/libc-help/2015-02/msg00022.html "malloc_trim() behaviour", but there is still no reply from them. (Only wrong answers from other users like https://sourceware.org/ml/libc-help/2015-05/msg00007.html https://sourceware.org/ml/libc-help/2015-05/msg00008.html)

    Or you may test the malloc_trim with this simple C program (test_malloc_trim.c) and 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_trim, strace ./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
    

    So, there is madvise with MADV_DONTNEED for 9 pages after malloc_trim(0) call, when there was hole of 40008 bytes in the middle of the heap.

    0 讨论(0)
  • 2021-01-01 06:26

    ... utilizing madvise(x, MADV_DONTNEED) to release memory back to the operating system.

    madvise(x, MADV_DONTNEED) does not release memory. man madvise:

    MADV_DONTNEED
    Do not expect access in the near future. (For the time being, the application is finished with the given range, so the kernel can free resources associated with it.) Subsequent accesses of pages in this range will succeed, but will result either in reloading of the memory contents from the underlying mapped file (see mmap(2)) or zero-fill-on-demand pages for mappings without an underlying file.

    So, the usage of madvise(x, MADV_DONTNEED) does not contradict man malloc_trim's statement:

    This function cannot release free memory located at places other than the top of the heap.

    0 讨论(0)
提交回复
热议问题