En Python, la memoria se gestiona automáticamente: cuando se crea un objeto, se asigna memoria, y cuando ningún otro objeto se refiere a él, la memoria se libera. La estrategia principal es el conteo de referencias: cada objeto tiene un contador de referencias asociado, cuando este llega a cero, el objeto se elimina.
Sin embargo, pueden existir referencias cíclicas, donde los objetos se refieren entre sí, y su contador de referencias nunca baja a cero, incluso si las referencias externas han sido eliminadas.
Para combatir esto, Python (CPython) tiene un recolector de basura (garbage collector, GC) que detecta ciclos de objetos que no son accesibles desde las raíces y los elimina. El recolector de basura se puede gestionar desde el módulo gc.
Ejemplo de un ciclo:
class Node: def __init__(self): self.ref = None n1 = Node() n2 = Node() n1.ref = n2 n2.ref = n1 del n1, n2 # ¡los objetos aún viven debido a la referencia cíclica!
Pregunta: "Si se llama a del obj, la memoria siempre se liberará de inmediato. ¿Es esto cierto?"
Respuesta: ¡No! La operación del solo elimina una referencia al objeto. Si hay otras referencias (ya sean explícitas o cíclicas) el objeto no podrá ser eliminado hasta que el contador de referencias llegue a cero y/o el GC no elimine los ciclos. Ejemplo:
import gc class A: pass x = A() y = x del x # ¡y tiene una referencia restante, el objeto sigue vivo! del y # solo ahora el objeto puede ser eliminado
Historia
En un sistema de procesamiento de grandes archivos se mantenían en memoria objetos relacionados. Después de eliminar las referencias externas, la memoria aún no se liberaba. La razón — referencias cíclicas que no se limpiaban de inmediato y, a veces, no se limpiaban del todo debido a la existencia del destructor __del__.
Historia
En un proceso de servidor de larga duración, a menudo se creaban grandes estructuras temporales con gráficos cíclicos. Los desarrolladores no desactivaron la salida de depuración del GC, lo que provocó pausas raras pero notables en segundo plano. Se diagnosticaron como "caídas de rendimiento". Resultó que la culpable era la frecuente activación del recolector de basura.
Historia
Al portar un gran proyecto a PyPy (un intérprete alternativo), el código antiguo confiaba en el comportamiento específico de CPython de limpieza instantánea de memoria a través del conteo de referencias; en PyPy, los objetos no se eliminaban y limpiaban de inmediato, sino a "petición" del GC, lo que llevó a un comportamiento impredecible con archivos abiertos y conexiones de red.