La vera risposta è che l'unico modo per rendere sicuro ed efficiente il meccanismo di garbage collection è di avere supporto a livello di linguaggio per riferimenti opachi. (O, al contrario, una mancanza di supporto a livello di linguaggio per la manipolazione diretta della memoria.)
Java e C # possono farlo perché hanno tipi di riferimento speciali che non possono essere manipolati. Questo dà al runtime la libertà di fare cose come spostare gli oggetti allocati nella memoria , che è fondamentale per un'implementazione GC ad alte prestazioni.
Per la cronologia, nessuna implementazione GC moderna utilizza il conteggio dei riferimenti , quindi è completamente un'aringa rossa. I GC moderni utilizzano la raccolta generazionale, dove le nuove allocazioni sono trattate essenzialmente allo stesso modo in cui le allocazioni dello stack sono in un linguaggio come C ++, e periodicamente tutti gli oggetti allocati di recente che sono ancora vivi vengono spostati in uno spazio "sopravvissuto" separato e un'intera generazione di oggetti è deallocato in una volta.
Questo approccio ha vantaggi e svantaggi: l'aspetto positivo è che le allocazioni dell'heap in una lingua che supporta GC sono veloci quanto le allocazioni dello stack in una lingua che non supporta GC, e lo svantaggio è che gli oggetti che devono essere ripuliti prima di essere distrutti richiedono un meccanismo separato (ad es. la parola chiave using
di C #), altrimenti il loro codice di pulizia viene eseguito in modo non deterministico.
Si noti che una chiave per un GC ad alte prestazioni è che deve esistere un supporto linguistico per una classe speciale di riferimenti. C non ha questo supporto linguistico e non lo farà mai; poiché il C ++ ha un sovraccarico dell'operatore, potrebbe emulare un tipo di puntatore GC, anche se dovrebbe essere fatto con attenzione. Infatti, quando Microsoft ha inventato il loro dialetto di C ++ che sarebbe eseguito sotto CLR (il runtime .NET), è stato necessario inventare una nuova sintassi per "riferimenti in stile C #" (ad esempio Foo^
) per distinguerli da "C ++ - riferimenti di stile "(ad es. Foo&
).
Ciò che C ++ ha, e ciò che viene regolarmente utilizzato dai programmatori C ++, è puntatori intelligenti , che sono in realtà solo un meccanismo di conteggio dei riferimenti. Non considererei il conteggio dei riferimenti come GC "vero", ma offre molti degli stessi vantaggi, a costo di prestazioni più lente rispetto alla gestione manuale della memoria o al vero GC, ma con il vantaggio della distruzione deterministica.
Alla fine della giornata, la risposta si riduce a una funzionalità di progettazione del linguaggio. C ha fatto una scelta, il C ++ ha fatto una scelta che gli ha permesso di essere retrocompatibile con C pur fornendo alternative che sono abbastanza buone per la maggior parte degli scopi, e Java e C # hanno fatto una scelta diversa che è incompatibile con C ma è anche abbastanza buona per la maggior parte degli scopi. Sfortunatamente, non esiste un proiettile argentato, ma conoscere le diverse opzioni disponibili ti aiuterà a scegliere quello giusto per qualsiasi programma tu stia attualmente cercando di costruire.