In Python, la memoria viene gestita automaticamente: quando un oggetto viene creato, viene allocata memoria, e quando non ci sono più riferimenti a quell'oggetto da parte di altri oggetti, la memoria viene liberata. La strategia principale è il conteggio dei riferimenti: a ogni oggetto è associato un contatore di riferimenti, quando questo diventa zero, l'oggetto viene eliminato.
Tuttavia, possono verificarsi riferimenti circolari, quando gli oggetti si riferiscono l'uno all'altro, e il loro contatore di riferimenti non scende mai a zero, anche se i riferimenti dal codice esterno sono stati rimossi.
Per combattere questo in Python (CPython) c'è un garbage collector (GC), che rileva i cicli di oggetti non accessibili dalle radici e li elimina. Il garbage collector può essere gestito dal modulo gc.
Esempio di ciclo:
class Nodo: def __init__(self): self.ref = None n1 = Nodo() n2 = Nodo() n1.ref = n2 n2.ref = n1 del n1, n2 # gli oggetti sono ancora vivi a causa del riferimento circolare!
Domanda: "Se si chiama del obj, la memoria sarà sempre liberata immediatamente. È vero?"
Risposta: No! L'operazione del rimuove solo un riferimento all'oggetto. Se ci sono altri riferimenti (espliciti o circolari) da qualche parte, la memoria non verrà liberata finché il contatore di riferimenti non diventa zero e/o il GC non elimina i cicli. Esempio:
import gc class A: pass x = A() y = x del x # y ha ancora un riferimento, l'oggetto è vivo! del y # solo ora l'oggetto può essere eliminato
Storia
Nel sistema di elaborazione di grandi file venivano mantenuti in memoria oggetti collegati. Dopo la rimozione dei riferimenti esterni, la memoria non veniva comunque liberata. La ragione era riferimenti circolari che non venivano puliti immediatamente e talvolta non venivano affatto puliti a causa della presenza di un distruttore __del__.
Storia
In un processo server a lungo termine venivano spesso create grandi strutture temporanee con grafi ciclici. Gli sviluppatori non disattivarono l'output temporaneo di debug del GC, causando pause rare ma significative in background. Diagnosticato come "calo delle prestazioni". Si scoprì che la causa era l'esecuzione frequente del garbage collector.
Storia
Durante la portazione di un grande progetto su PyPy (un interprete alternativo), il codice vecchio si basava sul comportamento specifico di CPython per la pulizia immediata della memoria tramite il conteggio dei riferimenti - in PyPy, gli oggetti venivano eliminati e puliti non immediatamente, ma a "richiesta" del GC, portando a comportamenti imprevedibili con file aperti e connessioni di rete.