c# - Qual è lo scopo di GC.SuppressFinalize(questo) nel metodo Dispose()?




.net garbage-collection (4)

Ho il codice seguente:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}

Anche se c'è un commento che spiega lo scopo di quella chiamata relativa al GC, non capisco ancora perché sia ​​lì.

L'oggetto non è destinato alla garbage collection una volta che tutte le istanze cessano di esistere, come quando vengono utilizzate using blocco?

Qual è lo scenario del caso d'uso in cui questo svolgerebbe un ruolo importante?


Da MSDN: sample :

Questo metodo imposta un po 'nell'intestazione dell'oggetto, che il sistema controlla quando chiama i finalizzatori. Il parametro obj deve essere il chiamante di questo metodo.

Gli oggetti che implementano l'interfaccia IDisposable possono chiamare questo metodo dal metodo IDisposable .. ::. Dispose per impedire al garbage collector di chiamare Object .. ::. Finalize su un oggetto che non lo richiede.

Normalmente lo useresti se il tuo oggetto non fa riferimento ad altri oggetti, solo tipi discreti, o ha già ripristinato qualsiasi riferimento a NULL.


Gli oggetti che possono essere finalizzati sopravvivono alla prima esecuzione del GC.

Normalmente, quando il GC rileva che un oggetto è irraggiungibile, lo reclama. Se l'oggetto è definibile, allora il GC non lo reclamerà; invece, considera comunque raggiungibile (e tutti gli oggetti a cui questo oggetto fa riferimento, e così via) e lo pianifica per la finalizzazione. L'oggetto sarà recuperato solo quando sarà di nuovo irraggiungibile ad un certo punto dopo che è stato finalizzato.

Ciò significa che un oggetto finalizzabile comporta un costo aggiuntivo: l'oggetto deve essere tenuto in memoria per un tempo più lungo. Da qui la chiamata che vedi: vale la pena sopprimere la finalizzazione quando non è necessaria. Qui, l'oggetto usa la finalizzazione per assicurarsi che sia sempre "smaltito" ad un certo punto. Quando è disposto in modo esplicito, non ha più bisogno di essere finalizzato.


Se il tuo tipo implementa un finalizzatore ( ~MyType() { } ), impedisce al garbage collector di eseguirlo. Utilizzato quando il finalizzatore si occupa di tipi non gestiti, ma l'utente ha già chiamato Dispose() (esplicitamente o tramite un blocco using() { } ), liberando quei tipi non gestiti.


Garbage collection : GC recupera la memoria utilizzata dall'oggetto quando l'oggetto non viene più referenziato.

Dispose : un metodo dall'interfaccia IDisposable che consente di rilasciare tutte le risorse gestite e non gestite quando il programmatore lo chiama (direttamente o indirettamente tramite un blocco using).

Finalizzatore : un metodo per rilasciare tutte le risorse non gestite. Chiamato dal GC prima di reclamare la memoria.

Risorsa gestita : qualsiasi classe .NET che implementa l'interfaccia IDisposable , come Streams e DbConnections.

Risorsa non gestita: il ripieno avvolto nelle classi di risorse gestite. Le maniglie di Windows sono gli esempi più banali.

Ora, per rispondere alla tua domanda:

GC mantiene una lista (Finalization Queue) di tutti gli oggetti la cui classe dichiara un Finalizer (~ ClassName in C #). Gli oggetti sono messi in questa coda alla creazione. GC viene eseguito periodicamente per verificare se ci sono oggetti inaccessibili dal programma. Quindi controlla se uno qualsiasi degli oggetti inaccessibili fa riferimento alla Coda di finalizzazione e li inserisce in un'altra coda chiamata coda Freacheable, mentre il resto viene recuperato. Un thread separato viene utilizzato per eseguire i metodi Finalize degli oggetti nella coda Freacheable.

La prossima volta che GC è in esecuzione, scoprirà che alcuni degli oggetti precedentemente nella coda Freacheable sono già finalizzati, quindi sono pronti per il recupero. Nota che GC ha bisogno di almeno due cicli (o molto di più se c'è un sacco di Finalizzazione da fare) per sbarazzarsi di un oggetto con un Finalizer, che incorre in penalità prestazionali.

Il metodo SuppressFinalize imposta semplicemente un flag nell'intestazione dell'oggetto che indica che non è necessario eseguire Finalizer. In questo modo GC può recuperare immediatamente la memoria dell'oggetto. Come per la definizione di cui sopra, il metodo Dispose fa la stessa cosa di Finalizer (e altro), quindi se viene eseguito, la finalizzazione non è più necessaria. Usando il metodo SuppressFinalize , puoi risparmiare del lavoro per il GC notificandolo a riguardo. Inoltre, ora non è necessario implementare controlli in Finalizer per evitare il doppio rilascio. L'unico problema con Dispose è che non è garantito l'esecuzione, perché è responsabilità del programmatore chiamarlo, ecco perché a volte abbiamo bisogno di preoccuparci di Finalizer.

Detto questo, è molto raro che tu abbia bisogno di scrivere un Finalizer, perché per la stragrande maggioranza delle solite risorse non gestite esiste già un wrapper gestito e le risorse gestite devono essere rilasciate chiamando i loro metodi Dispose dal tuo Dispose metodo, e da lì solo! Nei finalizzatori non devi mai chiamare un metodo Dispose.

Ulteriori letture :





suppressfinalize