Python gestionează memoria automat folosind două mecanisme: numărarea referințelor (metoda principală) plus un colector de gunoi ciclic care tratează ciclurile de referințe. Nu trebuie niciodată să eliberezi manual memoria, dar înțelegerea modului în care funcționează explică comportamentul memoriei și scurgerile.
Numărarea referințelor — mecanismul principal
import sys
x = [1, 2, 3] # the list object has refcount 1
y = x # refcount 2 (another name references it)
sys.getrefcount(x) # shows the count (slightly higher due to the call itself)
del y # refcount 1
del x # refcount 0 → the object is FREED immediately
Fiecare obiect ține evidența câte referințe îl indică. Când numărătorul scade la zero, memoria este eliberată imediat. Acesta este determinist și prompt — obiectele dispar imediat cum nu mai sunt referențiate de nimic.
Problema: ciclurile de referințe
# reference counting alone can't free this:
a = {}
b = {}
a["b"] = b # a references b
b["a"] = a # b references a → they reference EACH OTHER
del a, b # the names are gone, but the objects still reference each other
# → refcounts never reach 0 → they'd leak with refcounting alone
Două obiecte care se referențiază reciproc mențin refcount-ul fiecăruia deasupra zeroului chiar și atunci când sunt inaccesibile — un ciclu pe care pura numărare a referințelor nu poate să-l colecteze.
Colectorul de gunoi ciclic
import gc
# the generational GC periodically finds and frees unreachable CYCLES
gc.collect() # manually trigger a collection
gc.get_count() # objects tracked per generation
Colectorul de gunoi generațional suplimentar al Python-ului detectează și eliberează periodic aceste cicluri inaccesibile. Este generațional — obiectele mai noi sunt verificate mai des (majoritatea obiectelor mor tinere), ceea ce este eficient.
Implicații practice și surse de scurgeri
✓ Most memory is reclaimed promptly via reference counting
⚠️ Leaks still happen from REACHABLE references that outlive their usefulness:
- global caches/lists that only grow
- objects held by long-lived closures or class attributes
- registered callbacks/listeners never removed
GC-ul tratează ciclurile, dar nu poate elibera obiecte care sunt încă accesibile — "scurgerea" comună din lumea reală este menținerea involuntară a referințelor active (de exemplu, o cache globală în creștere constantă).
Alte detalii
- Small integers (-5..256) and short strings are CACHED/interned (shared, reused)
- CPython uses memory pools (pymalloc) for small objects (efficiency)
De ce este important
Înțelegerea modelului de memorie al Python-ului — numărarea promptă a referințelor plus un GC ciclic — explică comportamentele reale: de ce obiectele sunt de obicei eliberate imediat, de ce ciclurile de referințe au nevoie de GC, și crucial, de ce "scurgerile de memorie" în Python provin din referințe accesibile (cache-uri în creștere, closuri rămase, callback-uri neîndepărtate) mai degrabă decât din eliberări uitate.
Această cunoaștere este esențială pentru diagnosticarea creșterii memoriei în serviciile de lungă durată și pentru scrierea codului conștient de memorie — știind că soluția este de obicei întreruperea referințelor (limitarea cache-urilor, curățarea înregistrărilor) mai degrabă decât ceva ce GC-ul poate face pentru tine.
