it-swarm.com.ru

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

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

Каково в этом отношении поведение:

  • ptmalloc 1, 2 (по умолчанию glibc) или 3
  • dlmalloc
  • tcmalloc (Google резьбовой Malloc)
  • солярис 10-11 по умолчанию malloc и mtmalloc
  • FreeBSD 8 по умолчанию malloc (jemalloc)
  • Клад Маллок?

Обновление

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

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

54
osgx

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

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

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

    Такое же поведение может быть достигнуто путем установки порогового значения обрезки в переменной окружения 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.

31
Shashi

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

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

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

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

15
Alex Martelli

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

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

    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();
    

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

5
Laurent Debricon

У меня была похожая проблема в моем приложении, после некоторого исследования я заметил, что по какой-то причине 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
5
marcinH

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

4
dkantowitz

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

4
Andrew McGregor

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

3
exa