Вернут ли реализации malloc свободную память обратно в систему? - PullRequest
54 голосов
/ 07 февраля 2010

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

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

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

Обновление

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

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

Ответы [ 7 ]

31 голосов
/ 05 октября 2010

Следующий анализ относится только к 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 байтов в конце, и завершается ошибкой, если можно освободить байты размером менее одной страницы. Размер сегмента всегда кратен одной странице, что составляет 4 096 байт на i386.

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

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

15 голосов
/ 07 февраля 2010

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

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

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

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

5 голосов
/ 15 декабря 2013

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

Я имею дело с той же проблемой, что и ОП. Пока что это возможно с помощью 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();
    

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

4 голосов
/ 07 февраля 2010

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

4 голосов
/ 07 февраля 2010

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

3 голосов
/ 23 мая 2013

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

...