Wann sollte ich GC.SuppressFinalize () verwenden?

287

Unter welchen Umständen sollte ich in .NET verwenden GC.SuppressFinalize()?

Welche Vorteile bringt mir diese Methode?

GEOCHET
quelle
Ich habe ein paar Fragen zu Finalisierern und IDisposable gesehen, Stackoverflow sollte auch etwas über GC.SupressFinalize und schwache Referenzen haben
Sam Saffron
Ich denke nicht, dass schwache Referenzen in Bezug auf Finalizer viel bewirken - vielleicht sollten Sie eine direktere Frage dazu stellen.
Michael Burr
Yerp Ich wollte eine separate Frage zu schwachen Refs stellen. All dies kann zusammenpassen, wenn Sie Objektpools erstellen. Ich sollte auch eine Frage zur Objektwiederbelebung stellen, ala ReRegisterForFinalize
Sam Saffron

Antworten:

296

SuppressFinalizesollte nur von einer Klasse aufgerufen werden, die einen Finalizer hat. Es informiert den Garbage Collector (GC) darüber, dass das thisObjekt vollständig bereinigt wurde.

Das empfohlene IDisposableMuster, wenn Sie einen Finalizer haben, ist:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

Normalerweise überwacht die CLR Objekte mit einem Finalizer, wenn sie erstellt werden (was die Erstellung teurer macht). SuppressFinalizeteilt dem GC mit, dass das Objekt ordnungsgemäß bereinigt wurde und nicht in die Finalizer-Warteschlange gestellt werden muss. Es sieht aus wie ein C ++ - Destruktor, verhält sich aber nicht so.

Die SuppressFinalizeOptimierung ist nicht trivial, da Ihre Objekte lange auf die Finalizer-Warteschlange warten können. Seien Sie nicht versucht, SuppressFinalizeandere Objekte anzurufen . Das ist ein schwerwiegender Defekt, der darauf wartet, passiert zu werden.

Die Entwurfsrichtlinien teilen uns mit, dass ein Finalizer nicht erforderlich ist, wenn Ihr Objekt implementiert IDisposablewird. Wenn Sie jedoch einen Finalizer haben, sollten Sie ihn implementieren IDisposable, um eine deterministische Bereinigung Ihrer Klasse zu ermöglichen.

Die meiste Zeit sollten Sie in der Lage sein, IDisposableRessourcen zu bereinigen. Sie sollten nur dann einen Finalizer benötigen, wenn Ihr Objekt nicht verwaltete Ressourcen enthält und Sie sicherstellen müssen, dass diese Ressourcen bereinigt werden.

Hinweis: Manchmal fügen Codierer einen Finalizer zum Debuggen von Builds ihrer eigenen IDisposableKlassen hinzu, um zu testen, ob der Code ihr IDisposableObjekt ordnungsgemäß entsorgt hat .

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif
Robert Paulson
quelle
1
Im ersten Code-Snippet poste ich nur, wie das empfohlene IDisposable + Finalizer-Muster aussieht. Das Debuggen von Code ist gut, kann aber ablenken. Ich kann nur empfehlen, Finalizer zu vermeiden, außer für Klassen mit nicht verwalteten Ressourcen. Das Schreiben von sicherem Finalizer-Code ist nicht trivial.
Robert Paulson
1
Hallo, warum müssen wir dispose mit false als Parameter vom Finalizer aufrufen? Was ist, wenn die Entsorgung nie aufgerufen wurde und dann nicht entsorgt wird? Was ist, wenn wir nur prüfen, ob das Objekt entsorgt wurde oder nicht, und die eigentliche Bereinigung durchführen?
Träumer
3
@Dreamer - das hängt von Ihrer Implementierung ab. Im Allgemeinen möchten Sie wissen, ob Dispose vom Finalizer im Vergleich zur Implementierung von IDisposable.Dispose () aufgerufen wird. Wenn Sie vom Finalizer aufgerufen werden, müssen Sie davon ausgehen, dass private Referenzen nicht mehr gültig sind und Sie wirklich nicht viel tun können. Wenn Sie jedoch von IDisposable.Dispose () aufgerufen werden, wissen Sie, dass Referenzen noch gültig sind.
Robert Paulson
32
Wenn dies bei der implementierten Klasse IDisposablenicht sealedder GC.SuppressFinalize(this) Fall ist , sollte sie den Aufruf von enthalten, auch wenn sie keinen benutzerdefinierten Finalizer enthält . Dies ist erforderlich, um eine ordnungsgemäße Semantik für abgeleitete Typen sicherzustellen, die einen benutzerdefinierten Finalizer hinzufügen, aber nur die geschützte Dispose(bool)Methode überschreiben .
Sam Harwell
1
sealedFür die abgeleiteten Klassen ist es wichtig, nicht wie von @SamHarwell erwähnt zu sein. CodeAnalysis führt zu ca1816 + ca1063, wenn die Klasse nicht versiegelt ist, versiegelte Klassen jedoch ohne SuppressFinalize.
Bindestrich
38

SupressFinalizeteilt dem System mit, dass die im Finalizer geleistete Arbeit bereits erledigt wurde, sodass der Finalizer nicht aufgerufen werden muss. Aus den .NET-Dokumenten:

Objekte, die die IDisposable-Schnittstelle implementieren, können diese Methode über die IDisposable.Dispose-Methode aufrufen, um zu verhindern, dass der Garbage Collector Object.Finalize für ein Objekt aufruft, für das dies nicht erforderlich ist.

Im Allgemeinen sollte fast jede Dispose()Methode in der Lage sein, aufzurufen GC.SupressFinalize(), da sie alles bereinigen sollte, was im Finalizer bereinigt werden würde.

SupressFinalizeDies ist nur eine Optimierung, die es dem System ermöglicht, das Objekt nicht in die Warteschlange des Finalizer-Threads zu stellen. Ein ordnungsgemäß geschriebener Dispose()/ Finalizer sollte ordnungsgemäß mit oder ohne Aufruf von funktionieren GC.SupressFinalize().

Michael Burr
quelle
2

Diese Methode muss für die DisposeMethode von Objekten aufgerufen werden, die das implementiert. Auf IDisposablediese Weise würde der GC den Finalizer nicht ein anderes Mal aufrufen, wenn jemand die DisposeMethode aufruft .

Siehe: GC.SuppressFinalize (Object) -Methode - Microsoft Docs

Albertein
quelle
9
Ich denke, "Muss" ist falsch - nicht einmal "sollte" - Es ist nur so, dass Sie in einigen Szenarien den Aufwand für das Einreihen / Finalisieren des Objekts eliminieren können.
Basic
1
Dispose(true);
GC.SuppressFinalize(this);

Wenn das Objekt über einen Finalizer verfügt, stellt .net eine Referenz in die Finalisierungswarteschlange.

Da wir einen Aufruf haben Dispose(ture), wird das Objekt gelöscht, sodass wir für diesen Job keine Finalisierungswarteschlange benötigen.

Rufen Sie also GC.SuppressFinalize(this)die Referenz zum Entfernen in der Finalisierungswarteschlange auf.

Max CHien
quelle
0

Wenn eine Klasse oder etwas davon abgeleitetes möglicherweise den letzten Live-Verweis auf ein Objekt mit einem Finalizer enthält, sollte das Objekt nach einer Operation, die von diesem Finalizer beeinträchtigt werden könnte , entweder aufgerufen werden GC.SuppressFinalize(this)oder GC.KeepAlive(this)aufgerufen werden, um sicherzustellen, dass der Finalizer gewonnen hat wird erst ausgeführt, nachdem dieser Vorgang abgeschlossen ist.

Die Kosten für GC.KeepAlive()und GC.SuppressFinalize(this)sind in jeder Klasse, die keinen Finalizer hat, im Wesentlichen gleich, und Klassen, die Finalizer haben, sollten im Allgemeinen aufgerufen GC.SuppressFinalize(this)werden. Daher ist die Verwendung der letzteren Funktion als letzter Schritt von Dispose()möglicherweise nicht immer erforderlich, wird es aber nicht falsch liegen.

Superkatze
quelle