Mecanismo de Recolección de Basura en Python

Comprensión de Pools de Objetos Enteros y Mecanismo Interno

Pools de Enteros Pequeños

Los enteros se utilizan ampliamente en los programas. Para optimizar el rendimiento, Python emplea un pool de objetos para enteros pequeños, evitando la asignación y liberación frecuentes de memoria.

Python define enteros pequeños en el rango [-5, 257). Estos objetos enteros se crean de antemano y no se recolectan como basura. En un programa Python, todos los enteros dentro de este rango comparten el mismo objeto.

De manera similar, los caracteres individuales también siguen este patrón. Sin embargo, cuando se definen dos cadenas idénticas, su conteo de referencias es 0, lo que activa la recolección de basura.

Pools de Enteros Grandes

En un terminal, cada ejecución crea nuevos objetos, mientras que en entornos como PyCharm, todo el código se carga en memoria como una unidad. Esto permite un pool de objetos para enteros grandes dentro del mismo bloque de código, compartiendo objetos idénticos.

Mecanismo Interno

Consideremos el siguiente ejemplo de código:

cadena1 = "PythonGC"
cadena2 = "PythonGC"
cadena3 = "PythonGC"
cadena4 = "PythonGC"
cadena5 = "PythonGC"
cadena6 = "PythonGC"
cadena7 = "PythonGC"
cadena8 = "PythonGC"
cadena9 = "PythonGC"

Python no crea nueve objetos separados; utiliza el mecanismo interno para que todas las variables apunten a la misma instancia en memoria. Esto se basa en el conteo de referencias para determinar cuándo liberar el objeto.

Recolección de Basura (GC) en Python

Lenguajes modernos como Java o C# utilizan mecanismos de recolección de basura, a diferenica de C o C++ donde la gestión de memoria es manual. Python adopta un enfoque híbrido: el conteo de referencias como método principal, complementado con recolección generacional y marcado-eliminación.

Conteo de Referencias

En Python, cada objeto tiene un contador de referencias integrado en su estructura. Este contador aumenta cuando se crea una nueva referencia y disminuye cuando se elimina una referencia.

Por ejemplo, la estructura de un objeto puede definirse conceptualmente como:

struct ObjetoPy {
    int contador_ref;
    struct tipo_objeto *tipo;
};

Cuando el contador de referencias llega a cero, el objeto se libera automáticamente.

Ventajas:

  • Simplicidad en la implementación.
  • Liberación inmediata de memoria cuando no hay referencias.

Desventajas:

  • Consumo de recursos para mantener el conteo.
  • Problemas con referencias circulares.

Referencias Circulares

Las referencias circulares ocurren cuando dos o más objetos se refieren entre sí, manteniendo sus contadores de referencias por encima de cero incluso cuando ya no están en uso. Por ejemplo:

listaA = []
listaB = []
listaA.append(listaB)
listaB.append(listaA)

Esto provoca fugas de memoria, ya que los objetos no se pueden liberar automáticamente.

Recolección Generacional

Para manejar referencias circulares, Python utiliza recolección generacional. Los objetos se dividen en generaciones (0, 1, 2), donde los objetos nuevos se asignan a la generación 0. Los objetos que sobreviven a la recolección se promocionan a generaciones superiores.

El recolector de basura ejecuta análisis más frecuentes en generaciones más jóvenes, basado en la hipótesis de que los objetos nuevos tienden a ser de corta vida.

Módulo gc

El módulo gc proporciona funciones para interactuar con el recolector de basura. Se utiliza principalmente para manejar referencias circulares.

Conteo de Referencias en Acción

Para observar el conteo de referencias, se puede usar el módulo sys:

import sys
objeto = "ejemplo"
print(sys.getrefcount(objeto))  # Muestra el conteo, incluyendo la referencia de la función

Fugas de Memia por Referencias Circulares

El siguiente ejemplo demuestra una fuga de memoria cuando el recolector de basura está deshabilitado:

import gc

class ClaseDemo:
    def __init__(self):
        print(f'Objeto creado, id: {hex(id(self))}')

def funcion_fuga():
    while True:
        instancia1 = ClaseDemo()
        instancia2 = ClaseDemo()
        instancia1.sig = instancia2
        instancia2.sig = instancia1
        del instancia1
        del instancia2

gc.disable()
funcion_fuga()

Uso del Módulo gc

El módulo gc ofrece funciones como collect() para iniciar la recolección y set_threshold() para ajustar los umbrales de recolección automática.

import gc

class ClaseDemo:
    def __init__(self):
        print(f'Objeto creado, id: {hex(id(self))}')

def demostrar_gc():
    gc.set_debug(gc.DEBUG_LEAK)
    obj1 = ClaseDemo()
    obj2 = ClaseDemo()
    obj1.sig = obj2
    obj2.sig = obj1
    del obj1
    del obj2
    print("Basura no alcanzable:", gc.collect())
    print("Objetos en gc.garbage:", gc.garbage)

if __name__ == '__main__':
    demostrar_gc()

La recolección automática se activa cuando se importa gc y está habilitada. Los objetos con métodos __del__ pueden causar problemas con la recolección de basura, por lo que se recomienda evitarlos cuando sea posible.

Etiquetas: Python Garbage Collection reference counting generational GC gc module

Publicado el 7-9 02:11