Gestión de la memoria

Visión general

La gestión de memoria en Python implica un montón privado que contiene todos los objetos de Python y estructuras de datos. El administrador de memoria de Python garantiza internamente la gestión de este montón privado. El administrador de memoria de Python tiene diferentes componentes que se ocupan de varios aspectos de la gestión dinámica del almacenamiento, como compartir, segmentación, asignación previa o almacenamiento en caché.

En el nivel más bajo, un asignador de memoria sin procesar asegura que haya suficiente espacio en el montón privado para almacenar todos los datos relacionados con Python al interactuar con el administrador de memoria del sistema operativo. Además del asignador de memoria sin procesar, varios asignadores específicos de objeto operan en el mismo montón e implementan políticas de administración de memoria distintas adaptadas a las peculiaridades de cada tipo de objeto. Por ejemplo, los objetos enteros se administran de manera diferente dentro del montón que las cadenas, tuplas o diccionarios porque los enteros implican diferentes requisitos de almacenamiento y compensaciones de velocidad / espacio. El administrador de memoria de Python delega parte del trabajo a los asignadores específicos de objeto, pero asegura que este último opere dentro de los límites del montón privado.

Es importante comprender que la gestión del montón de Python la realiza el propio intérprete y que el usuario no tiene control sobre él, incluso si manipulan regularmente punteros de objetos a bloques de memoria dentro de ese montón. El administrador de memoria de Python realiza la asignación de espacio de almacenamiento dinámico para los objetos de Python y otros búferes internos a pedido a través de las funciones de API de Python/C enumeradas en este documento.

Para evitar daños en la memoria, los escritores de extensiones nunca deberían intentar operar en objetos Python con las funciones exportadas por la biblioteca C: malloc(), calloc(), realloc() y free(). Esto dará como resultado llamadas mixtas entre el asignador de C y el administrador de memoria de Python con consecuencias fatales, ya que implementan diferentes algoritmos y operan en diferentes montones. Sin embargo, uno puede asignar y liberar de forma segura bloques de memoria con el asignador de la biblioteca C para fines individuales, como se muestra en el siguiente ejemplo:

PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

En este ejemplo, la solicitud de memoria para el búfer de E/S es manejada por el asignador de la biblioteca C. El administrador de memoria de Python solo participa en la asignación del objeto de bytes retornado como resultado.

Sin embargo, en la mayoría de las situaciones, se recomienda asignar memoria del montón de Python específicamente porque este último está bajo el control del administrador de memoria de Python. Por ejemplo, esto es necesario cuando el intérprete se amplía con nuevos tipos de objetos escritos en C. Otra razón para usar el montón de Python es el deseo de informar al administrador de memoria de Python sobre las necesidades de memoria del módulo de extensión. Incluso cuando la memoria solicitada se usa exclusivamente para fines internos y altamente específicos, delegar todas las solicitudes de memoria al administrador de memoria de Python hace que el intérprete tenga una imagen más precisa de su huella de memoria en su conjunto. En consecuencia, bajo ciertas circunstancias, el administrador de memoria de Python puede o no desencadenar acciones apropiadas, como recolección de basura, compactación de memoria u otros procedimientos preventivos. Tenga en cuenta que al usar el asignador de la biblioteca C como se muestra en el ejemplo anterior, la memoria asignada para el búfer de E/S escapa completamente al administrador de memoria Python.

Ver también

La variable de entorno PYTHONMALLOC puede usarse para configurar los asignadores de memoria utilizados por Python.

La variable de entorno PYTHONMALLOCSTATS se puede utilizar para imprimir estadísticas de asignador de memoria pymalloc cada vez que se crea un nuevo escenario de objetos pymalloc, y en el apagado.

Interfaz de memoria sin procesar

Los siguientes conjuntos de funciones son envoltorios para el asignador del sistema. Estas funciones son seguras para subprocesos, no es necesario mantener el GIL.

El asignador de memoria sin procesar predeterminado usa las siguientes funciones: malloc(), calloc(), realloc() y free(); llame a malloc(1) (o calloc(1, 1)) cuando solicita cero bytes.

Nuevo en la versión 3.4.

void* PyMem_RawMalloc(size_t n)

Allocates n bytes and returns a pointer of type void* to the allocated memory, or NULL if the request fails.

Solicitar cero bytes retorna un puntero distinto que no sea NULL si es posible, como si en su lugar se hubiera llamado a PyMem_RawMalloc(1). La memoria no se habrá inicializado de ninguna manera.

void* PyMem_RawCalloc(size_t nelem, size_t elsize)

Allocates nelem elements each whose size in bytes is elsize and returns a pointer of type void* to the allocated memory, or NULL if the request fails. The memory is initialized to zeros.

Solicitar elementos cero o elementos de tamaño cero bytes retorna un puntero distinto NULL si es posible, como si en su lugar se hubiera llamado PyMem_RawCalloc(1, 1).

Nuevo en la versión 3.5.

void* PyMem_RawRealloc(void *p, size_t n)

Cambia el tamaño del bloque de memoria señalado por p a n bytes. Los contenidos no se modificarán al mínimo de los tamaños antiguo y nuevo.

Si p es NULL, la llamada es equivalente a PyMem_RawMalloc(n); de lo contrario, si n es igual a cero, el bloque de memoria cambia de tamaño pero no se libera, y el puntero retornado no es NULL.

A menos que p sea NULL, debe haber sido retornado por una llamada previa a PyMem_RawMalloc(), PyMem_RawRealloc() o PyMem_RawCalloc().

Si la solicitud falla, PyMem_RawRealloc() retorna NULL y p sigue siendo un puntero válido al área de memoria anterior.

void PyMem_RawFree(void *p)

Libera el bloque de memoria al que apunta p, que debe haber sido retornado por una llamada anterior a PyMem_RawMalloc(), PyMem_RawRealloc() o PyMem_RawCalloc(). De lo contrario, o si se ha llamado antes a PyMem_RawFree(p), se produce un comportamiento indefinido.

Si p es NULL, no se realiza ninguna operación.

Interfaz de memoria

Los siguientes conjuntos de funciones, modelados según el estándar ANSI C, pero que especifican el comportamiento cuando se solicitan cero bytes, están disponibles para asignar y liberar memoria del montón de Python.

El asignador de memoria predeterminado usa el asignador de memorya pymalloc.

Advertencia

El GIL debe mantenerse cuando se utilizan estas funciones.

Distinto en la versión 3.6: El asignador predeterminado ahora es pymalloc en lugar del malloc() del sistema.

void* PyMem_Malloc(size_t n)

Allocates n bytes and returns a pointer of type void* to the allocated memory, or NULL if the request fails.

Solicitar cero bytes retorna un puntero distinto que no sea NULL si es posible, como si en su lugar se hubiera llamado a PyMem_Malloc(1). La memoria no se habrá inicializado de ninguna manera.

void* PyMem_Calloc(size_t nelem, size_t elsize)

Allocates nelem elements each whose size in bytes is elsize and returns a pointer of type void* to the allocated memory, or NULL if the request fails. The memory is initialized to zeros.

Solicitar elementos cero o elementos de tamaño cero bytes retorna un puntero distinto NULL si es posible, como si en su lugar se hubiera llamado PyMem_Calloc(1, 1).

Nuevo en la versión 3.5.

void* PyMem_Realloc(void *p, size_t n)

Cambia el tamaño del bloque de memoria señalado por p a n bytes. Los contenidos no se modificarán al mínimo de los tamaños antiguo y nuevo.

Si p es NULL, la llamada es equivalente a PyMem_Malloc(n); de lo contrario, si n es igual a cero, el bloque de memoria cambia de tamaño pero no se libera, y el puntero retornado no es NULL.

A menos que p sea NULL, debe haber sido retornado por una llamada previa a PyMem_Malloc(), PyMem_Realloc() o PyMem_Calloc().

Si la solicitud falla, PyMem_Realloc() retorna NULL y p sigue siendo un puntero válido al área de memoria anterior.

void PyMem_Free(void *p)

Libera el bloque de memoria señalado por p, que debe haber sido retornado por una llamada anterior a PyMem_Malloc(), PyMem_Realloc() o PyMem_Calloc(). De lo contrario, o si se ha llamado antes a PyMem_Free(p), se produce un comportamiento indefinido.

Si p es NULL, no se realiza ninguna operación.

Las siguientes macros orientadas a tipos se proporcionan por conveniencia. Tenga en cuenta que TYPE se refiere a cualquier tipo de C.

TYPE* PyMem_New(TYPE, size_t n)

Same as PyMem_Malloc(), but allocates (n * sizeof(TYPE)) bytes of memory. Returns a pointer cast to TYPE*. The memory will not have been initialized in any way.

TYPE* PyMem_Resize(void *p, TYPE, size_t n)

Same as PyMem_Realloc(), but the memory block is resized to (n * sizeof(TYPE)) bytes. Returns a pointer cast to TYPE*. On return, p will be a pointer to the new memory area, or NULL in the event of failure.

Esta es una macro de preprocesador C; p siempre se reasigna. Guarde el valor original de p para evitar perder memoria al manejar errores.

void PyMem_Del(void *p)

La misma que PyMem_Free().

Además, se proporcionan los siguientes conjuntos de macros para llamar al asignador de memoria de Python directamente, sin involucrar las funciones de API de C mencionadas anteriormente. Sin embargo, tenga en cuenta que su uso no conserva la compatibilidad binaria entre las versiones de Python y, por lo tanto, está en desuso en los módulos de extensión.

  • PyMem_MALLOC(size)

  • PyMem_NEW(type, size)

  • PyMem_REALLOC(ptr, size)

  • PyMem_RESIZE(ptr, type, size)

  • PyMem_FREE(ptr)

  • PyMem_DEL(ptr)

Asignadores de objetos

Los siguientes conjuntos de funciones, modelados según el estándar ANSI C, pero que especifican el comportamiento cuando se solicitan cero bytes, están disponibles para asignar y liberar memoria del montón de Python.

El asignador predeterminado de objetos usa el asignador de memoria pymalloc.

Advertencia

El GIL debe mantenerse cuando se utilizan estas funciones.

void* PyObject_Malloc(size_t n)

Allocates n bytes and returns a pointer of type void* to the allocated memory, or NULL if the request fails.

Solicitar cero bytes retorna un puntero distinto que no sea NULL si es posible, como si en su lugar se hubiera llamado a PyObject_Malloc(1). La memoria no se habrá inicializado de ninguna manera.

void* PyObject_Calloc(size_t nelem, size_t elsize)

Allocates nelem elements each whose size in bytes is elsize and returns a pointer of type void* to the allocated memory, or NULL if the request fails. The memory is initialized to zeros.

Solicitar elementos cero o elementos de tamaño cero bytes retorna un puntero distinto NULL si es posible, como si en su lugar se hubiera llamado PyObject_Calloc(1, 1).

Nuevo en la versión 3.5.

void* PyObject_Realloc(void *p, size_t n)

Cambia el tamaño del bloque de memoria señalado por p a n bytes. Los contenidos no se modificarán al mínimo de los tamaños antiguo y nuevo.

Si p es NULL, la llamada es equivalente a PyObject_Malloc(n); de lo contrario, si n es igual a cero, el bloque de memoria cambia de tamaño pero no se libera, y el puntero retornado no es NULL.

A menos que p sea NULL, debe haber sido retornado por una llamada previa a PyObject_Malloc(), PyObject_Realloc() o PyObject_Calloc().

Si la solicitud falla, PyObject_Realloc() retorna NULL y p sigue siendo un puntero válido al área de memoria anterior.

void PyObject_Free(void *p)

Libera el bloque de memoria al que apunta p, que debe haber sido retornado por una llamada anterior a PyObject_Malloc(), PyObject_Realloc() o PyObject_Calloc(). De lo contrario, o si se ha llamado antes a PyObject_Free(p), se produce un comportamiento indefinido.

Si p es NULL, no se realiza ninguna operación.

Asignadores de memoria predeterminados

Asignadores de memoria predeterminados:

Configuración

Nombre

PyMem_RawMalloc

PyMem_Malloc

PyObject_Malloc

Lanzamiento de compilación

"pymalloc"

malloc

malloc + debug

malloc + debug

Compilación de depuración

"pymalloc_debug"

malloc + debug

pymalloc + debug

pymalloc + debug

Lanzamiento de compilación, sin pymalloc

"malloc"

malloc

malloc

malloc

Compilación de depuración, sin pymalloc

"malloc_debug"

malloc + debug

malloc + debug

malloc + debug

Leyenda:

Personalizar asignadores de memoria

Nuevo en la versión 3.4.

PyMemAllocatorEx

Estructura utilizada para describir un asignador de bloque de memoria. La estructura tiene cuatro campos:

Campo

Significado

void *ctx

contexto de usuario pasado como primer argumento

void* malloc(void *ctx, size_t size)

asignar un bloque de memoria

void* calloc(void *ctx, size_t nelem, size_t elsize)

asignar un bloque de memoria inicializado con ceros

void* realloc(void *ctx, void *ptr, size_t new_size)

asignar o cambiar el tamaño de un bloque de memoria

void free(void *ctx, void *ptr)

liberar un bloque de memoria

Distinto en la versión 3.5: La estructura PyMemAllocator se renombró a PyMemAllocatorEx y se agregó un nuevo campo calloc.

PyMemAllocatorDomain

Enum se utiliza para identificar un dominio asignador. Dominios:

PYMEM_DOMAIN_RAW

Funciones:

PYMEM_DOMAIN_MEM

Funciones:

PYMEM_DOMAIN_OBJ

Funciones:

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Obtenga el asignador de bloque de memoria del dominio especificado.

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Establece el asignador de bloque de memoria del dominio especificado.

El nuevo asignador debe retornar un puntero distinto NULL al solicitar cero bytes.

Para el dominio PYMEM_DOMAIN_RAW, el asignador debe ser seguro para subprocesos: el GIL no se mantiene cuando se llama al asignador.

Si el nuevo asignador no es un enlace (no llama al asignador anterior), se debe llamar a la función PyMem_SetupDebugHooks() para reinstalar los enlaces de depuración en la parte superior del nuevo asignador.

void PyMem_SetupDebugHooks(void)

Configurar ganchos para detectar errores en las funciones del asignador de memoria de Python.

La memoria recién asignada se llena con el byte 0xCD (CLEANBYTE), la memoria liberada se llena con el byte 0xDD (DEADBYTE). Los bloques de memoria están rodeados por «bytes prohibidos» (FORBIDDENBYTE: byte 0xFD).

Verificaciones de tiempo de ejecución:

En caso de error, los enlaces de depuración usan el módulo tracemalloc para obtener el rastreo donde se asignó un bloque de memoria. El rastreo solo se muestra si tracemalloc rastrea las asignaciones de memoria de Python y se rastrea el bloque de memoria.

Estos enlaces son instalado por defecto si Python se compila en modo de depuración. La variable de entorno PYTHONMALLOC puede usarse para instalar enlaces de depuración en un Python compilado en modo de lanzamiento.

Distinto en la versión 3.6: Esta función ahora también funciona en Python compilado en modo de lanzamiento. En caso de error, los enlaces de depuración ahora usan tracemalloc para obtener el rastreo donde se asignó un bloque de memoria. Los enlaces de depuración ahora también verifican si el GIL se mantiene cuando se llaman a las funciones de PYMEM_DOMAIN_OBJ y dominios PYMEM_DOMAIN_MEM.

Distinto en la versión 3.8: Los patrones de bytes 0xCB (CLEANBYTE), 0xDB (DEADBYTE) y 0xFB (FORBIDDENBYTE) han sido reemplazados por 0xCD, 0xDD y 0xFD para usar los mismos valores que la depuración CRT de Windows malloc() y free().

El asignador pymalloc

Python tiene un asignador pymalloc optimizado para objetos pequeños (más pequeños o iguales a 512 bytes) con una vida útil corta. Utiliza asignaciones de memoria llamadas «arenas» con un tamaño fijo de 256 KiB. Vuelve a PyMem_RawMalloc() y PyMem_RawRealloc() para asignaciones de más de 512 bytes.

pymalloc es el asignador por defecto de PYMEM_DOMAIN_MEM (por ejemplo: PyMem_Malloc()) y PYMEM_DOMAIN_OBJ (por ejemplo: PyObject_Malloc()) dominios.

El asignador de arena utiliza las siguientes funciones:

  • VirtualAlloc() y VirtualFree() en Windows,

  • mmap() y munmap() si está disponible,

  • malloc() y free() en caso contrario.

Personalizar asignador de arena de pymalloc

Nuevo en la versión 3.4.

PyObjectArenaAllocator

Estructura utilizada para describir un asignador de arena. La estructura tiene tres campos:

Campo

Significado

void *ctx

contexto de usuario pasado como primer argumento

void* alloc(void *ctx, size_t size)

asignar una arena de bytes de tamaño

void free(void *ctx, void *ptr, size_t size)

liberar la arena

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

Consigue el asignador de arena.

void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

Establecer el asignador de arena.

tracemalloc C API

Nuevo en la versión 3.7.

int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)

Rastree un bloque de memoria asignado en el módulo tracemalloc.

Retorna 0 en caso de éxito, retorna -1 en caso de error (no se pudo asignar memoria para almacenar la traza). Retorna -2 si tracemalloc está deshabilitado.

Si el bloque de memoria ya está rastreado, actualice el rastreo existente.

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)

Descomprima un bloque de memoria asignado en el módulo tracemalloc. No haga nada si el bloque no fue rastreado.

Retorna -2 si tracemalloc está deshabilitado; de lo contrario, retorna 0.

Ejemplos

Aquí está el ejemplo de la sección Visión general, reescrito para que el búfer de E/S se asigne desde el montón de Python utilizando el primer conjunto de funciones:

PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

El mismo código que utiliza el conjunto de funciones orientado a tipos:

PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Del(buf); /* allocated with PyMem_New */
return res;

Tenga en cuenta que en los dos ejemplos anteriores, el búfer siempre se manipula a través de funciones que pertenecen al mismo conjunto. De hecho, es necesario usar la misma familia de API de memoria para un bloque de memoria dado, de modo que el riesgo de mezclar diferentes asignadores se reduzca al mínimo. La siguiente secuencia de código contiene dos errores, uno de los cuales está etiquetado como fatal porque mezcla dos asignadores diferentes que operan en montones diferentes.:

char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3);  /* Wrong -- should be PyMem_Free() */
free(buf2);       /* Right -- allocated via malloc() */
free(buf1);       /* Fatal -- should be PyMem_Del()  */

Además de las funciones destinadas a manejar bloques de memoria sin procesar del montón de Python, los objetos en Python se asignan y liberan con PyObject_New(), PyObject_NewVar() y PyObject_Del() .

Esto se explicará en el próximo capítulo sobre cómo definir e implementar nuevos tipos de objetos en C.