Что на самом деле означает "malloc_trim(0)"?

Страница руководства рассказала мне так много, и благодаря ей я знаю множество базовых знаний по управлению памятью в "glibc".

Но я все еще запутался. Означает ли " malloc_trim (0) " (обратите внимание, ноль в качестве параметра) значение (1.) вся память в разделе "куча" будет возвращена ОС? Или (2.) просто вся "неиспользованная" память самой верхней области кучи будет возвращена ОС?

Если ответ (1.), что если все еще используемая память в куче? если куча где-то использовала momery, будут ли они удалены или функция не будет выполнена успешно?

Хотя, если ответ (2.), как насчет этих " дыр " в местах, а не наверху в куче - они больше не используются, но по-прежнему используется самая верхняя область кучи, будет ли этот вызов работать эффективно?

Благодарю.

2 ответа

Страница человека из malloc_trim было совершено здесь: https://github.com/mkerrisk/man-pages/blob/master/man3/malloc_trim.3 и, как я понимаю, оно было написано разработчиком man-страниц, Kerrisk в 2012 году с нуля: https://github.com/mkerrisk/man-pages/commit/a15b0e60b297e29c825b7417582a33e6ca26bf65

Поскольку я могу использовать git для glibc, в glibc нет man-страниц, и нет никакой ссылки на man-страницу malloc_trim для документирования этого патча. Лучшей и единственной документацией к glibc malloc является его исходный код: https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.cmalloc_trim комментарии от 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 и не документировано в проекте man-pages. Справочная страница 2012 года может быть первой справочной страницей функции, написанной не авторами glibc. На информационной странице glibc упоминается только M_TRIM_THRESHOLD размером 128 КБ: https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html и не перечисляются функции malloc_trim https://www.gnu.org/software/libc/manual/html_node/Summary-of-Malloc.html (и это также не документирует memusage/memusagestat/libmemusage.so).

В декабре 2007 года Ульрих Дреппер ( https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=68631c8eb92ff38d9da1ae34f6aa048539b199cc был создан Ульрихом Дреппером (он является частью glibc 2.9 и новее).) который изменился mtrim реализация (но это не изменило никакой документации или man-страницы, так как в glibc нет man-страниц):

  • malloc / malloc.c (public_mTRIm): перебрать все арены и вызвать

мТРИМ для них всех. (mTRIm): дополнительно переберите все свободные блоки и используйте madvise для освобождения памяти для всех тех блоков, которые содержат хотя бы одну страницу памяти.

Неиспользуемые части чанков (где угодно, включая чанки посередине), выровненные по размеру страницы и имеющие размер больше страницы, могут быть помечены как MADV_DONTNEED https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=c54c203cbf1f024e72493546221305b4fd5729b7;hp=1e716089a2b976d120c304ad75dd95c63737ad75;hb=68631c8eb92ff38d9da1ae34f6aa048539b199cc;hpb=52386be756e113f20502f181d780aecc38cbb66a

       INTERNAL_SIZE_T size = chunksize (p);

       if (size > psm1 + sizeof (struct malloc_chunk))
         {
           /* 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);
        }

Это одно из двух madvise с MADV_DONTNEED в glibc теперь один для верхней части кучи (shrink_heap) и прочее это маркировка любого куска (mtrim): 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);

Мы можем проверить malloc_trim с помощью этой простой программы на C (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);
    // check that all arrays are allocated on the heap and not with mmap
    printf("1:%p 2:%p 3:%p 4:%p\n", m1, m2, m3, m4);
    // free 40000 bytes in the middle
    free(m2);
    // call trim (same result with 2000 or 2000000 argument)
    malloc_trim(0);
    // call some syscall to find this point in the strace output
    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
...
nanosleep({1, 0}, 0x7ffffafbfff0)       = 0
brk(0xceb000)                           = 0xceb000

Итак, было madvise с MADV_DONTNEED за 9 страниц после malloc_trim(0) вызов, когда в середине кучи была дыра в 40008 байт.

Справочная страница для malloc_trim говорит, что освобождает свободную память, поэтому, если в куче есть выделенная память, она не освобождает всю кучу. Параметр есть, если вы знаете, что вам по-прежнему понадобится определенный объем памяти, поэтому освобождение большего объема приведет к тому, что glibc позже придется выполнять ненужную работу.

Что касается дыр, это стандартная проблема с управлением памятью и возвратом памяти в ОС. Основное низкоуровневое управление кучей, доступное программе: brk а также sbrkи все, что они могут сделать, это увеличить или уменьшить область кучи, изменив верхнюю часть. Так что у них нет возможности вернуть дыры в операционную систему; как только программа позвонила sbrk чтобы выделить больше кучи, это пространство может быть возвращено только в том случае, если вершина этого пространства свободна и может быть возвращена.

Обратите внимание, что существуют другие, более сложные способы выделения памяти (с анонимным mmapнапример), которые могут иметь другие ограничения, чем sbrkРаспределение

Другие вопросы по тегам