Будут ли реализации malloc возвращать свободную память обратно в систему?


У меня есть долгоживущее приложение с частым выделением памяти-освобождением. Будет ли реализация функции malloc возвращает освобожденную память обратно в систему?

что, в этом отношении поведение:

  • ptmalloc 1, 2 (в glibc по умолчанию) или 3
  • dlmalloc
  • tcmalloc (Google threaded malloc)
  • solaris 10-11 по умолчанию malloc и mtmalloc
  • FreeBSD 8 default malloc (jemalloc)
  • хорда Мэллок?

обновление

Если у меня есть приложение, потребление памяти которого может быть очень разным в дневное и ночное время (например), могу ли я заставить любого из malloc вернуть освобожденную память в систему?

без такого возврата освобожденная память будет перекачиваться и во много раз, но такая память содержит только мусор.

7   51  

7 ответов:

следующий анализ применяется только к glibc (на основе алгоритма ptmalloc2). Есть определенные параметры, которые кажутся полезными для возврата освобожденной памяти обратно в систему:

  1. mallopt() (определена в malloc.h) предоставляет возможность установить пороговое значение обрезки, используя один из параметров option M_TRIM_THRESHOLD, это указывает на минимальный объем свободной памяти (в байтах), разрешенный в верхней части сегмента данных. Если сумма падает ниже этого порог, в glibc вызывает brk(), чтобы вернуть память к ядру.

    значение по умолчанию M_TRIM_THRESHOLD в Linux установлено значение 128K, установка меньшего значения может сэкономить место.

    то же самое поведение может быть достигнуто путем установки порогового значения обрезки в переменной среды MALLOC_TRIM_THRESHOLD_, без каких-либо изменений источника абсолютно.

    однако предварительные тестовые программы выполняются с использованием M_TRIM_THRESHOLD показал, что даже если память, выделенная malloc, возвращается к система, оставшаяся часть фактического куска памяти (Арена), первоначально запрошенная через brk() имеет тенденцию сохраняться.

  2. можно обрезать арену памяти и вернуть любую неиспользуемую память в систему, позвонив malloc_trim(pad) (определена в malloc.h). Эта функция изменяет размер сегмента данных, оставляя по крайней мере pad байты в конце его и сбой, если можно освободить менее одной страницы байтов. Размер сегмента всегда кратен одной странице, которая это 4096 байт на i386.

    реализация для этого измененного поведения free() используя malloc_trim может быть сделано с помощью функции malloc и крючком. Это не потребует каких-либо изменений исходного кода в основной библиотеке glibc.

  3. используя madvise() системный вызов внутри свободной реализации glibc.

большинство реализаций не утруждают себя идентификацией тех (относительно редких) случаев, когда целые "блоки" (любого размера, подходящего для ОС) были освобождены и могут быть возвращены, но есть, конечно, исключения. Например, и я цитирую из страница Википедии, в OpenBSD:

по вызову память и удалены из адресной процесс пространство с помощью munmap. Эта система предназначен для повышения безопасности путем принятия преимущество макет адресного пространства рандомизация и разрыв страницы особенности реализован как часть операционной системы OpenBSD mmap системный вызов, и обнаружить использование-после-свободных ошибок - в качестве большой памяти распределение полностью не сопоставлено после его освобождения, дальнейшее использование вызывает ошибка сегментации и завершение программы.

большинство систем не так ориентированы на безопасность, как OpenBSD.

зная это, когда я кодирую давно работающую систему, которая имеет известное-быть-переходное требование для большого объема памяти, я всегда стараюсь fork процесс: родитель затем просто ждет результатов от ребенка [[обычно на трубе]], ребенок выполняет вычисления (включая выделение памяти), возвращает результаты [[на указанной трубе]], а затем завершается. Таким образом, мой длительный процесс не будет бесполезно забивать память в течение долгого времени между случайными "всплесками" в ее требовании к памяти. Другие альтернативные стратегии включают в себя переход на пользовательский распределитель памяти для таких специальных требований (C++ делает его достаточно простым, хотя языки с виртуальными машинами под ним, такие как Java и Python, обычно этого не делают).

Я имею дело с такой же проблемой как ОП. До сих пор, представляется возможным с помощью tcmalloc. Я нашел два решения:

  1. скомпилируйте свою программу с помощью tcmalloc linked, а затем запустите ее как:

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    документация отмечает, что

    разумные цены находятся в диапазоне [0,10].

    но 10 мне кажется недостаточно (т. е. я не вижу никаких изменений).

  2. найти где-то в вашем коде, где было бы интересно освободить всю освобожденную память, а затем добавить этот код:

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    

второе решение было очень эффективным в моем случае; первое было бы здорово, но это не очень успешно, трудно найти правильный номер, например.

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

из тех, которые вы перечисляете, только Hoard вернет память в систему... но если он действительно может это сделать, это будет во многом зависеть от поведения распределения вашей программы.

у меня была аналогичная проблема в моем приложении, после некоторого исследования я заметил, что по какой-то причине glibc не возвращает память в систему, когда выделенные объекты малы (в моем случае менее 120 байт).
Посмотрите на этот код:

#include <list>
#include <malloc.h>

template<size_t s> class x{char x[s];};

int main(int argc,char** argv){
    typedef x<100> X;

    std::list<X> lx;
    for(size_t i = 0; i < 500000;++i){
        lx.push_back(X());
    }

    lx.clear();
    malloc_stats();

    return 0;
}

программа:

Arena 0:
system bytes     =   64069632
in use bytes     =          0
Total (incl. mmap):
system bytes     =   64069632
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

около 64 МБ не возвращаются в систему. Когда я изменил typedef на: typedef x<110> X; вывод программы выглядит так:

Arena 0:
system bytes     =     135168
in use bytes     =          0
Total (incl. mmap):
system bytes     =     135168
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

почти вся память была освобождена. Я также заметил, что используя malloc_trim(0) в любом случае освобождается память для системы.
Вот вывод после добавления malloc_trim в приведенном выше коде:

Arena 0:
system bytes     =       4096
in use bytes     =          0
Total (incl. mmap):
system bytes     =       4096
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

короткий ответ: чтобы заставить подсистему malloc вернуть память в ОС, используйте malloc_trim(). В противном случае поведение возвращаемой памяти зависит от реализации.