Il termine Garbage Collector è ultimamente tornato nel vocabolario dei programmatori, essendo questo tipo di funzionalità supportata da quasi tutti i linguaggi di programmazione e sistemi di sviluppo: da Java a Visual Basic e la piattaforma del .NET framework. Notevole eccezione a questa regola è il linguaggio C/C++, nel quale la gestione della memoria è a totale carico del programmatore. La storia della Garbage Collection non è però affatto recente, difatti è stata implementata per la prima volta con l’interprete LISP e questo tipo di sistemi sono stati sempre integrati nei linguaggi di sviluppo fin dagli anni ’60.
La Garbage Collection è la raccolta dei rifiuti, cioè tutte le porzioni di memoria allocate, ma non più utilizzate. Un Garbage Collector è un sistema che a run-time si occupa di ricercare queste porzioni di memoria e di renderle di nuovo disponibili per essere utilizzate. Un meccanismo di questo tipo è utile se il linguaggio consente di dichiarare variabili in modo dinamico, la cui allocazione avviene durante l’esecuzione del programma. Consideriamo la seguente porzione di codice C/C++:
int x;
int* p = new int;
L’allocazione della variabile x nella prima riga di codice è statica, il suo tempo di vita è definito a priori e può essere determinato all’atto della compilazione del sorgente. Ad esempio, se la variabile viene definita all’inizio di una funzione, essa viene allocata all’inizio e deallocata alla fine della stessa ( non è proprio così, ma non abbiamo bisogno di entrare nei dettagli in questo momento ). L’allocazione di p invece, nella seconda riga di codice, è dinamica e viene effettuata solo quando l’istruzione viene effettivamente eseguita. L’istruzione potrebbe rientrare in un if oppure non sapere dello spazio in memoria da allocare perché di tipo non identificabile. Un oggetto allocato dinamicamente non ha una data di nascita nota a priori e non può essere deallocato solo perché non viene più utilizzato dal programma. Da questo punto di vista esistono solo due possibilità, o come in C/C++ che sia il programmatore a decidere quando l’oggetto debba essere deallocato o con il Garbage Collector che controlli l’esecuzione del programma e verifichi quando un oggetto non è più raggiungibile e quindi possa essere deallocato.
Generalizzando possiamo affermare che un oggetto può essere rilasciato quando non esistono più variabili che permettono di accedervi, in C/C++ questo avviene con i puntatori, mentre in linguaggi come Java, VB.NET o C# si utilizzano riferimenti, ma il concetto non varia. Per rilasciare un oggetto, quindi, occorre essere sicuri che non possa più essere utilizzato. Esistono diverse tecniche per effettuare questo tipo di controllo, a cui corrispondono diverse strategie di funzionamento di un Garbage Collector. Gli oggetti non referenziati potranno essere cancellati, ma questi a loro volta potrebbero referenziarli altri e così via. Questo tipo di controllo risulterebbe quindi estremamente dispendioso e per questo viene utilizzato il reference counting, in cui in ogni oggetto creato viene mantenuto un contatore che indica la quantità di riferimenti esistenti a quell’oggetto, così come funzionano gli oggetti COM ( Component Object Model ) con AddRef e Release. Ogni volta che si assegna ad una variabile un riferimento ad un oggetto, il reference count dell’oggetto viene incrementato, mentre quando una variabile cessa di esistere, il reference count dell’oggetto referenziato viene decrementato, quando il contatore è arrivato a zero il Garbage Collector distrugge l’oggetto, ma come vedremo qualche problemino rimane.
In C/C++ la maggior parte degli errori di programmazione risiede nella scorretta gestione dei puntatori e nell’accesso diretto alla memoria. Quando si programma in C/C++ il più grosso problema, anche perché poco individuabile, è il memory leak, che è per l’appunto l’occupazione della memoria e non liberata. Se il memory leak risiede in un ciclo, il problema potrebbe essere molto grave, perché si occuperebbe molta memoria inutilmente e questo può essere grave anche nei computer attuali, che hanno molta RAM.
In Visual Basic per liberare un oggetto si utilizza la seguente nozione:
Set p = Nothing
In questo caso l’oggetto non viene rilasciato immediatamente dal Garbage Collector, ma solo in seguito, quando questo si accorge che non è più utilizzato. Nel .NET framework l’algoritmo utilizzato per il Garbage Collector non è più il reference counting, ma viene considerata l’effettiva raggiungibilità dell’oggetto stesso, quindi non si deve più utilizzare nessuna istruzione per rilasciare esplicitamente i riferimenti. Nel .NET però il rilascio dell’oggetto non è immediato, anzi occorre aspettare del tempo affinché l’oggetto venga distrutto.
Il Garbage Collector può essere d’aiuto, ma bisogna conoscere a fondo la specifica implementazione sul sistema che si sta utilizzando per non esserne danneggiati.