¿Las implementaciones malloc devolverán la memoria de edición libre al sistema?


Tengo una aplicación de larga duración con asignación de memoria frecuente-desasignación. ¿Alguna implementación de malloc devolverá la memoria liberada al sistema?

¿Cuál es, a este respecto, el comportamiento de:

  • ptmalloc 1, 2 (glibc por defecto) o 3
  • dlmalloc
  • tcmalloc (google threaded malloc)
  • solaris 10-11 por defecto malloc y mtmalloc
  • FreeBSD 8 por defecto malloc (jemalloc)
  • Hoard malloc?

Actualización

Si tengo una aplicación cuyo consumo de memoria puede ser muy diferente durante el día y la noche (por ejemplo), ¿puedo forzar a cualquiera de los malloc a devolver la memoria liberada al sistema?

Sin tal retorno, la memoria liberada se intercambiará y en muchas ocasiones, pero dicha memoria contiene solo basura.

Author: osgx, 2010-02-07

7 answers

El siguiente análisis se aplica solo a glibc (basado en el algoritmo ptmalloc2). Hay ciertas opciones que parecen útiles para devolver la memoria liberada al sistema:

  1. Mallopt () (definido en malloc.h) proporciona una opción para establecer el valor de umbral de trim utilizando uno de los parámetros option M_TRIM_THRESHOLD, esto indica la cantidad mínima de memoria libre (en bytes) permitida en la parte superior del segmento de datos. Si la cantidad cae por debajo de este umbral, glibc invoca brk() para devolver memoria al núcleo.

    El valor predeterminado de M_TRIM_THRESHOLD en Linux es 128K, establecer un valor más pequeño podría ahorrar espacio.

    El mismo comportamiento se podría lograr estableciendo el valor de umbral trim en la variable de entorno MALLOC_TRIM_THRESHOLD_, sin cambios de fuente absolutamente.

    Sin embargo, los programas de prueba preliminares que se ejecutan usando M_TRIM_THRESHOLD han demostrado que a pesar de que la memoria asignada por malloc vuelve al sistema, la porción restante del trozo real de la memoria (la arena) inicialmente solicitada a través de brk() tiende a conservarse.

  2. Es posible recortar la arena de memoria y devolver cualquier memoria no utilizada al sistema llamando a malloc_trim(pad) (definido en malloc.h). Esta función redimensiona el segmento de datos, dejando al menos pad bytes al final del mismo y fallando si se puede liberar menos de una página de bytes. El tamaño del segmento es siempre un múltiplo de una página, que es de 4.096 bytes en i386.

    La implementación de esta modificación el comportamiento de free() usando malloc_trim podría hacerse usando la funcionalidad de gancho malloc. Esto no requeriría ningún cambio en el código fuente de la biblioteca central de glibc.

  3. Usando madvise() llamada al sistema dentro de la implementación libre de glibc.

 29
Author: Shashi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-12 15:12:05

La mayoría de las implementaciones no se molestan en identificar aquellos casos (relativamente raros) donde "bloques" enteros (de cualquier tamaño que se adapte al sistema operativo) han sido liberados y podrían ser devueltos, pero hay, por supuesto, excepciones. Por ejemplo, y cito de la página de wikipedia , en OpenBSD:

En una llamada a free, la memoria se libera y sin asignar desde la dirección del proceso espacio usando munmap. Este sistema es diseñado para mejorar la seguridad tomando ventaja del espacio de direcciones diseño características de la página de aleatorización y brecha implementado como parte de la mmap llamada al sistema, y para detectar usar-después-errores libres - como una memoria grande la asignación está completamente sin asignar después de que se libera, otras causas de uso un fallo de segmentación y terminación del programa.

Sin embargo, la mayoría de los sistemas no están tan centrados en la seguridad como OpenBSD.

Sabiendo esto, cuando estoy codificando un sistema de larga duración que tiene un requisito conocido como transitorio para una gran cantidad de memoria, siempre intento fork el proceso: el padre entonces solo espera los resultados del hijo [[típicamente en una tubería]], el hijo hace el cálculo (incluyendo la asignación de memoria), devuelve los resultados [[en dicha tubería]], luego termina. De esta manera, mi proceso de larga duración no estará acaparando inútilmente la memoria durante los largos tiempos entre "picos" ocasionales en su demanda de memoria. Otras estrategias alternativas incluyen cambiar a un asignador de memoria personalizado para tales requisitos especiales (C++ hace que sea razonablemente fácil, aunque los lenguajes con máquinas virtuales debajo, como Java y Python, generalmente no lo hacen).

 15
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-07 00:34:43

Estoy tratando con el mismo problema que el OP. Hasta ahora, parece posible con tcmalloc. Encontré dos soluciones:

  1. Compila tu programa con tcmalloc linked, luego ejecútalo como:

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    La documentación menciona que

    Las tasas razonables están en el rango [0,10].

    Pero 10 no me parece suficiente (es decir, no veo ningún cambio).

  2. Encuentre algún lugar en su código donde sería interesante liberar todo la memoria liberada, y luego añadir este código:

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

La segunda solución ha sido muy efectiva en mi caso; la primera sería genial pero no es muy exitosa, es complicado encontrar el número correcto por ejemplo.

 5
Author: Laurent Debricon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-12 15:07:43

Para todos los mallocs 'normales', incluidos los que ha mencionado, la memoria se libera para ser reutilizada por su proceso, pero no para todo el sistema. La liberación de nuevo a todo el sistema ocurre solo cuando el proceso se termina finalmente.

 4
Author: dkantowitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-07 00:02:02

De los que enumere, solo Hoard devolverá memoria al sistema... pero si realmente puede hacer eso dependerá mucho del comportamiento de asignación de su programa.

 4
Author: Andrew McGregor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-07 00:27:18

Tuve un problema similar en mi aplicación, después de algunas investigaciones me di cuenta de que por alguna razón glibc no devuelve la memoria al sistema cuando los objetos asignados son pequeños (en mi caso menos de 120 bytes).
Mira este código:

#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;
}

Salida del programa:

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

Alrededor de 64 MB no se devuelven al sistema. Cuando cambié typedef a: typedef x<110> X; la salida del programa se ve así:

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

Casi toda la memoria fue liberada. También me di cuenta de que el uso de malloc_trim(0) en cualquier caso liberado memoria a sistema.
Aquí está la salida después de agregar malloc_trim al código anterior:

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
 4
Author: marcinH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-15 19:12:38

La respuesta corta: Para forzar que el subsistema malloc devuelva memoria al sistema operativo, use malloc_trim(). De lo contrario, el comportamiento de la memoria devuelta depende de la implementación.

 2
Author: exa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-23 18:25:19