Conceptos básicos de la gestión de memoria en Python

La administración de la memoria es el proceso de asignar, desasignar y coordinar la memoria de manera eficiente para que todos los diferentes procesos se ejecuten sin problemas y puedan optar...

Introducción

La administración de la memoria es el proceso de asignar, desasignar y coordinar la memoria de manera eficiente para que todos los diferentes procesos se ejecuten sin problemas y puedan acceder de manera óptima a los diferentes recursos del sistema. La gestión de la memoria también implica limpiar la memoria de los objetos a los que ya no se accede.

En Python, el administrador de memoria es responsable de este tipo de tareas ejecutándose periódicamente para limpiar, asignar y administrar la memoria. A diferencia de C, Java y otros lenguajes de programación, Python administra objetos mediante el recuento de referencias. Esto significa que el administrador de memoria realiza un seguimiento del número de referencias a cada objeto en el programa. Cuando el recuento de referencias de un objeto cae a cero, lo que significa que el objeto ya no se usa, el recolector de basura (parte del administrador de memoria) libera automáticamente la memoria de ese objeto en particular.

El usuario no necesita preocuparse por la gestión de la memoria, ya que el proceso de asignación y desasignación de memoria es totalmente automático. La memoria recuperada puede ser utilizada por otros objetos.

Recolección de basura de Python

Como se explicó anteriormente, Python elimina objetos a los que ya no se hace referencia en el programa para liberar espacio en la memoria. Este proceso en el que Python libera bloques de memoria que ya no se usan se llama Garbage Collection. El Python Recolector de basura (GC) se ejecuta durante la ejecución del programa y se activa si el recuento de referencias se reduce a cero. El recuento de referencias aumenta si a un objeto se le asigna un nuevo nombre o si se coloca en un contenedor, como una tupla o un diccionario. Del mismo modo, el recuento de referencias disminuye cuando se reasigna la referencia a un objeto, cuando la referencia del objeto pasa a fuera del ámbito, o cuando se elimina un objeto. .

La memoria es un montón que contiene objetos y otras estructuras de datos utilizadas en el programa. El administrador de memoria de Python controla la asignación y desasignación de este espacio de almacenamiento dinámico mediante el uso de funciones API.

Objetos de Python en memoria

Cada variable en Python actúa como un objeto. Los objetos pueden ser simples (que contienen números, cadenas, etc.) o contenedores (diccionarios, listas o clases definidas por el usuario). Además, Python es un lenguaje de tipo dinámico, lo que significa que no necesitamos declarar las variables o sus tipos antes de usarlas en un programa.

Por ejemplo:

1
2
3
4
5
6
7
8
9
>>> x = 5
>>> print(x)
5
>>> del x
>>> print(x)
Traceback (most reent call last):
  File "<mem_manage>", line 1, in <module>
    print(x)
NameError : name 'x' is not defined

Si observa las primeras 2 líneas del programa anterior, se conoce el objeto x. Cuando eliminamos el objeto x e intentamos usarlo, obtenemos un error que indica que la variable x no está definida.

Puede ver que la recolección de basura en Python está completamente automatizada y el programador no necesita preocuparse por eso, a diferencia de lenguajes como C.

Modificación del recolector de basura {#modificandoelrecolector de basura}

El recolector de basura de Python tiene tres generaciones en las que se clasifican los objetos. Un nuevo objeto en el punto de partida de su ciclo de vida es la primera generación del recolector de basura. A medida que el objeto sobreviva a la recolección de basura, se trasladará a las próximas generaciones. Cada una de las 3 generaciones del recolector de basura tiene un umbral. Específicamente, cuando se supera el umbral del número de asignaciones menos el número de desasignaciones, esa generación ejecutará la recolección de elementos no utilizados.

Las generaciones anteriores también son basura recolectada con más frecuencia que las generaciones superiores. Esto se debe a que es más probable que se descarten los objetos más nuevos que los viejos.

El módulo gc incluye funciones para cambiar el valor de umbral, desencadenar un proceso de recolección de basura manualmente, deshabilitar el proceso de recolección de basura, etc. Podemos verificar los valores de umbral de diferentes generaciones del recolector de basura usando el método get_threshold():

1
2
import gc
print(gc.get_threshold())

Salida de muestra:

1
(700, 10, 10)

Como ves, aquí tenemos un umbral de 700 para la primera generación y 10 para cada una de las otras dos generaciones.

Podemos alterar el valor del umbral para activar el proceso de recolección de basura usando el método set_threshold() del módulo gc:

1
gc.set_threshold(900, 15, 15)

En el ejemplo anterior, hemos aumentado el valor de umbral para las 3 generaciones. Aumentar el valor del umbral disminuirá la frecuencia de ejecución del recolector de elementos no utilizados. Normalmente, no necesitamos pensar demasiado en la recolección de basura de Python como desarrollador, pero esto puede ser útil al optimizar el tiempo de ejecución de Python para su sistema de destino. Uno de los beneficios clave es que el mecanismo de recolección de basura de Python maneja una gran cantidad de detalles de bajo nivel para el desarrollador automáticamente.

¿Por qué realizar la recolección manual de basura? {#por qué realizar la recolección manual de basura}

Sabemos que el intérprete de Python mantiene un registro de las referencias a los objetos utilizados en un programa. En versiones anteriores de Python (hasta la versión 1.6), el intérprete de Python usaba solo el mecanismo de conteo de referencias para manejar la memoria. Cuando el recuento de referencias cae a cero, el intérprete de Python libera automáticamente la memoria. Este mecanismo clásico de conteo de referencias es muy efectivo, excepto que no funciona cuando el programa tiene ciclos de referencia. Un ciclo de referencia ocurre si uno o más objetos se referencian entre sí y, por lo tanto, el recuento de referencia nunca llega a cero.

Consideremos un ejemplo.

1
2
3
4
5
6
7
>>> def create_cycle():
...     list = [8, 9, 10]
...     list.append(list)
...     return list
... 
>>> create_cycle()
[8, 9, 10, [...]]

El código anterior crea un ciclo de referencia, donde el objeto lista se refiere a sí mismo. Por lo tanto, la memoria para el objeto lista no se liberará automáticamente cuando la función regrese. El problema del ciclo de referencia no se puede resolver con el conteo de referencia. Sin embargo, este problema del ciclo de referencia se puede resolver cambiando el comportamiento del recolector de elementos no utilizados en su aplicación de Python.

Para hacerlo, podemos usar la función gc.collect() del módulo gc.

1
2
3
import gc
n = gc.collect()
print("Number of unreachable objects collected by GC:", n)

gc.collect() devuelve el número de objetos que ha recopilado y desasignado.

Hay dos formas de realizar la recolección de basura manual: recolección de basura basada en tiempo o basada en eventos.

La recolección de basura basada en el tiempo es bastante simple: la función gc.collect() se llama después de un intervalo de tiempo fijo.

La recolección de basura basada en eventos llama a la función gc.collect() después de que ocurre un evento (es decir, cuando se cierra la aplicación o la aplicación permanece inactiva durante un período de tiempo específico).

Comprendamos el trabajo manual de recolección de basura creando algunos ciclos de referencia.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys, gc

def create_cycle():
    list = [8, 9, 10]
    list.append(list)

def main():
    print("Creating garbage...")
    for i in range(8):
        create_cycle()

    print("Collecting...")
    n = gc.collect()
    print("Number of unreachable objects collected by GC:", n)
    print("Uncollectable garbage:", gc.garbage)

if __name__ == "__main__":
    main()
    sys.exit()

La salida es la siguiente:

1
2
3
4
Creating garbage...
Collecting...
Number of unreachable objects collected by GC: 8
Uncollectable garbage: []

La secuencia de comandos anterior crea un objeto de lista al que hace referencia una variable, denominada creativamente “lista”. El primer elemento del objeto de lista se refiere a sí mismo. El recuento de referencias del objeto de la lista siempre es mayor que cero, incluso si se elimina o queda fuera del alcance del programa. Por lo tanto, el objeto list no se recolecta como basura debido a la referencia circular. El mecanismo del recolector de elementos no utilizados en Python buscará y recopilará automáticamente referencias circulares periódicamente.

En el código anterior, dado que el recuento de referencias es al menos 1 y nunca puede llegar a 0, hemos recolectado los objetos a la fuerza llamando a gc.collect(). Sin embargo, recuerde no forzar la recolección de basura con frecuencia. La razón es que incluso después de liberar la memoria, el GC se toma un tiempo para evaluar la elegibilidad del objeto para ser recolectado como basura, lo que consume tiempo y recursos del procesador. Además, recuerde administrar manualmente el recolector de basura solo después de que su aplicación se haya iniciado por completo.

Conclusión

En este artículo, discutimos cómo la administración de memoria en Python se maneja automáticamente mediante el uso de estrategias de conteo de referencias y recolección de elementos no utilizados. Sin recolección de basura, es imposible implementar un mecanismo de administración de memoria exitoso en Python. Además, los programadores no deben preocuparse por eliminar la memoria asignada, ya que el administrador de memoria de Python se encarga de ello. Esto conduce a menos fugas de memoria y un mejor rendimiento. o.

Licensed under CC BY-NC-SA 4.0