Per me la semantica non ha importanza. Un programma perde se inizia a utilizzare un carico di memoria della barca e rallenta e rallenta più a lungo lo esegui, come un videogioco che richiede il riavvio ogni 30 minuti perché i frame rate continuano a scendere più a lungo si suona mentre va avanti da prendere megabyte a gigabyte di memoria. La maggior parte dei giochi che presentano questi sintomi che perdono usano la raccolta dei rifiuti e per una buona ragione: la raccolta dei rifiuti tende a scambiarsi immediatamente arresti riproducibili per perdite che volano sotto il radar.
In questo senso, in realtà può essere più facile introdurre perdite in un programma creato usando una lingua con GC, poiché tutto ciò che devi fare per eseguire il root di una risorsa e impedire che venga liberato è memorizzare un riferimento ad esso. / p>
Come esempio, supponiamo che tu abbia un videogioco che ha un sistema fisico per spostare particelle e un renderer per rendere un elenco di particelle a cui fa riferimento. Quando una particella muore, sfuma lo schermo e smette di essere visibile, a quel punto il sistema fisico lo rimuove dall'elenco delle particelle da elaborare.
Voilà, ora hai una perdita perché non hai rimosso il riferimento di particelle dal renderer. Tuttavia, non sarà ovvio in gioco perché le particelle morte hanno un'opacità pari a zero. Ciononostante, il gioco creerebbe una lista sempre più grande di particelle che non verranno mai liberate fino alla chiusura del gioco, e trascorreranno sempre più tempo in loop sempre più grandi nel renderer man mano che l'elenco delle particelle diventerà sempre più grande. Questo potrebbe volare sotto il radar degli sviluppatori indefinitamente al punto in cui in realtà suggeriscono agli utenti di riavviare il gioco di volta in volta se rallenta mentre si abbattono i requisiti di sistema con macchine potenti anche per un semplice gioco 2D.
Nel frattempo in C, questo avrebbe semplicemente portato ad un arresto immediatamente rilevabile dal momento che il sistema fisico avrebbe distrutto manualmente la particella quando sarebbe morta. Il renderer tenterebbe quindi di accedere a una particella che è stata distrutta e molto probabilmente si abbatterà durante il primo test di gioco che è probabilmente più preferibile in questo caso piuttosto che avere perdite di gioco che si interrompono indefinitamente.
Un modo molto risoluto per evitare questo problema è fare affidamento su concetti come i riferimenti deboli e fantasma e decidere chi effettivamente gestisce le particelle. Se si tratta del sistema fisico, il renderer dovrebbe mantenere riferimenti deboli alle particelle in modo che non prolunghino la durata della vita e possano rilevare quando vengono distrutti e si spera che finiscano in un errore di interruzione del gioco se il renderer tenta di accedere a una particella che non esiste più.
In generale, GC non ti protegge dal dover pensare alla gestione delle risorse e chi possiede una risorsa e dover liberare manualmente le risorse (assegnandole come none/nil/null
) per evitare perdite logiche. La sua utilità primaria a mio parere è nel contesto di aree come il multithreading in cui si desidera garantire che un oggetto non venga distrutto fino a quando un thread non ha finito di elaborare l'oggetto.
La soluzione ideale per me se un linguaggio potrebbe mai fornirlo a livello di lingua nativa (il C ++ è il più vicino che possa pensare con shared_ptr
, ma è un concetto di libreria e non può rilevare cicli poiché usa il conteggio di riferimento di base ) è uno che potrebbe consentire di optare per la garbage collection per oggetto. Ad esempio, forse l'oggetto viene normalmente distrutto quando esce dall'ambito o viene assegnato un valore null e tutto ciò che lo fa riferimento al di fuori del suo ambito immediato agisce come un puntatore, evitando che l'oggetto venga distrutto. Tuttavia, qualcosa che vuole condividere la proprietà di quell'oggetto, come un thread, potrebbe chiamare un metodo ref
per aumentare il suo conteggio di riferimento e deref
per decrementarlo per evitare che venga distrutto fino a quando il thread non lo utilizza, oppure potrebbe utilizzare una sorta di concetto di riferimento condiviso che evita la necessità di un esplicito deref
nell'ambito del thread, con un costo "paga come lo si utilizza".