Что на самом деле означает "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
Распределение