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

StackOverflow https://stackoverflow.com/questions/2215259

Вопрос

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

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

  • ptmalloc 1, 2 (glibc по умолчанию) или 3
  • длмаллок
  • tcmalloc (google threaded malloc)
  • solaris 10-11 по умолчанию malloc и mtmalloc
  • FreeBSD 8 malloc по умолчанию (jemalloc)
  • Копить маллок?

Обновить

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

FreeBSD 12-х годов malloc(3) использование джемаллок 5.1, который возвращает освобожденную память ("грязные страницы") в ОС, используя madvise(...MADV_FREE).

Освобожденная память возвращается только после временной задержки, контролируемой opt.dirty_decay_ms и opt.muzzy_decay_ms;смотрите на страница руководства и это проблема с реализацией очистки неиспользуемой грязной страницы на основе распада для получения более подробной информации.

Более ранние версии FreeBSD поставлялись со старыми версиями jemalloc, который также возвращает освобожденную память, но использует другой алгоритм для принятия решения о том, что очищать и когда.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top