"Determinante al 100%, una volta che non sono più utilizzati dovrebbero essere liberati immediatamente" - che è incompatibile con la garbage collection. Ma non è necessariamente in contrasto con la gestione automatica delle risorse.
Se le risorse devono essere liberate immediatamente, ciò significa che GC deve essere eseguito immediatamente ogni volta che viene rilasciato un riferimento. Una tale strategia è conteggio dei riferimenti . Il conteggio è un approccio di gestione delle risorse valido, ma presenta alcuni inconvenienti:
- Ogni oggetto gestito ha bisogno di un campo aggiuntivo per memorizzare il suo conteggio dei riferimenti.
- Il refcount deve essere modificato ogni volta che viene acquisito o rilasciato un riferimento.
- Il conteggio dei riferimenti deve essere modificato atomicamente, se l'oggetto può essere condiviso attraverso i thread.
- I riferimenti ciclici non possono essere rilasciati e richiedono invece un algoritmo di garbage collection completo.
A volte può essere determinato in fase di compilazione in cui una risorsa deve essere rilasciata. Questa è la base delle variabili automatiche in C ++ e del sistema di proprietà in Rust. La variante C ++ è ovviamente non valida perché la risorsa potrebbe essere rilasciata mentre è ancora referenziata. Ma poiché questi sistemi funzionano in fase di compilazione anziché in fase di runtime, non è sensato confrontarli con GC.
Una proprietà importante di questi sistemi è che esiste un concetto esplicito di proprietà. Una risorsa è associata a una particolare variabile. Non può essere copiato su un'altra variabile. Può essere spostato solo in modo permanente o prestato temporaneamente. Ciò è completamente diverso dagli oggetti in un sistema garbage-collected in cui è possibile copiare liberamente i riferimenti agli oggetti e la proprietà è condivisa tra tutti i riferimenti.
Quindi non è generalmente possibile combinare la gestione delle risorse deterministica automatica con la garbage collection. GC è principalmente preoccupato per la risorsa "memoria", che è comunemente usata in molti programmi. La memoria è fungibile: quei byte inutilizzati laggiù sono buoni quanto i byte occupati da questo oggetto forse morto. È molto più veloce utilizzare semplicemente la memoria senza memoria nota piuttosto che liberare immediatamente la memoria. Altre risorse non hanno questa proprietà, ad es. le prese dovrebbero essere chiuse in modo tempestivo. Quindi, se una lingua implica GC, questo è un compromesso: la gestione della memoria è resa sicura e veloce, al costo di dover gestire manualmente risorse non fungibili.
Se una lingua supportava le risorse in stile GC e RAII, queste risorse non possono essere soggette a GC. Quindi il linguaggio dovrebbe supportare due tipi distinti di oggetti. Ciò complica notevolmente il linguaggio in quanto questi due tipi di oggetti devono essere trattati staticamente come tipi diversi e non scambiabili. Questo non dovrebbe essere necessariamente una sintassi speciale come resource Foo f = acquireResource(); ...
, ma data la semantica drasticamente incompatibile tale sintassi sarebbe ragionevole. Per inciso, using (Foo f = acquireResource()) { ... }
in C # sembra essere esattamente tale sintassi e l'analisi statica può avvisarti se acquisisci una risorsa senza accertarti che sia stata ripulita. Questo alquanto svia le incompatibilità tra RAII e GC in quanto le risorse saranno rilasciate prima l'oggetto che rappresenta la risorsa può essere liberato. Lo svantaggio è che questo non è del tutto sano: puoi fare riferimento a una risorsa che è già stata rilasciata.