El recompte de referències (en anglès, reference counting) és una tècnica de gestió automàtica de la memòria dinàmica que consisteix a comptar quantes vegades es fa servir cada objecte allotjat a la memòria dinàmica (heap). Cada objecte porta associat un comptador que indica el nombre de referències actives que hi apunten des del programa. Quan una variable nova apunta a l'objecte, el comptador puja en una unitat; quan una referència desapareix, ja sigui perquè la variable surt de l'àmbit, es reassigna o s'elimina, el comptador baixa. Quan aquest valor arriba a zero, l'objecte esdevé inaccessible i la memòria s'allibera immediatament.
Context històric El recompte de referències va ser proposat formalment per George E. Collins el 1960 a l'article A Method for Overlapping and Erasure of Lists, publicat a la revista Communications of the ACM. Va ser una de les primeres tècniques automàtiques de gestió de memòria proposades, anterior als algorismes de traçat generacionals que dominen els entorns d'execució moderns com la màquina virtual de Java o el .NET runtime.
Funcionament i rendiment L'avantatge principal d'aquesta tècnica és la seva immediatesa. Tan bon punt una referència desapareix, la memòria queda lliure, sense necessitat d'esperar un procés periòdic de neteja. Això contrasta amb els algorismes de traçat (tracing garbage collection), que han d'aturar l'execució del programa per recórrer tota la memòria buscant objectes inaccessibles.
Tanmateix, aquest sistema té un cost constant: cada assignació o eliminació d'una referència obliga la màquina a actualitzar el comptador, fet que afegeix instruccions a operacions que altrament serien molt ràpides. En entorns amb múltiples fils d'execució, el problema s'agreuja, ja que dos fils podrien intentar modificar el mateix comptador alhora i provocar una condicions de cursa. Per evitar-ho, les actualitzacions s'han de fer de manera atòmica, normalment mitjançant instruccions de maquinari com compare-and-swap, que penalitzen el rendiment en aplicacions paral·leles.
Variants Existeixen diverses variants documentades del recompte de referències:
Recompte de referències no diferit: la variant clàssica, on cada modificació del comptador es fa de manera immediata. És la més senzilla però també la més costosa en aplicacions intensives. Recompte de referències diferit (deferred reference counting): retarda les actualitzacions del comptador per a referències locals a la pila, reduint la sobrecàrrega. One-bit reference counting: utilitza un únic bit per indicar si un objecte té una o més d'una referència, intercanviant precisió per estalvi de memòria.
Referències circulars La limitació més coneguda del recompte de referències és la incapacitat de detectar cicles. Es produeix un cicle quan dos o més objectes es referencien mútuament, per exemple, un objecte A apunta a B i, alhora, B apunta a A. Si el programa principal perd l'única referència externa al grup, el conjunt esdevé inaccessible, però els comptadors interns mai no arriben a zero perquè els objectes es mantenen vius els uns als altres. Aquesta situació provoca una fuita de memòria (memory leak).
Per resoldre aquest problema, els llenguatges que utilitzen recompte de referències solen combinar-lo amb altres mecanismes. Una solució habitual són les referències febles (weak references), que permeten accedir a un objecte sense incrementar-ne el comptador i, per tant, no impedeixen que sigui eliminat. Una altra opció és afegir un recol·lector de brossa auxiliar que detecti periòdicament els cicles aïllats.
Implementació a CPython L'intèrpret de referència de Python, CPython, utilitza el recompte de referències com a mecanisme principal de gestió de memòria. Cada objecte en CPython es representa internament amb una estructura anomenada PyObject, que conté un camp ob_refcnt on s'emmagatzema el comptador. Per garantir la integritat d'aquest camp davant accessos concurrents, CPython protegeix les operacions sobre el comptador mitjançant el Global Interpreter Lock (GIL), un bloqueig global que només permet a un fil executar codi Python alhora.
Aquesta dependència del GIL és una de les raons per les quals la concurrència basada en fils en Python no escala bé en tasques intensives en CPU. La proposta PEP 703, acceptada el 2023, planteja la possibilitat de fer el GIL opcional en futures versions de l'intèrpret, fet que requeriria reformular el sistema de recompte de referències per fer-lo segur sense el bloqueig global.
Vegeu també Recollida de memòria brossa Referència feble Gestió de memòria
Referències
Categoria:Programació