Unter welchen Umständen sollte ich in .NET verwenden GC.SuppressFinalize()
?
Welche Vorteile bringt mir diese Methode?
Unter welchen Umständen sollte ich in .NET verwenden GC.SuppressFinalize()
?
Welche Vorteile bringt mir diese Methode?
Antworten:
SuppressFinalize
sollte nur von einer Klasse aufgerufen werden, die einen Finalizer hat. Es informiert den Garbage Collector (GC) darüber, dass das this
Objekt vollständig bereinigt wurde.
Das empfohlene IDisposable
Muster, 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). SuppressFinalize
teilt 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 SuppressFinalize
Optimierung ist nicht trivial, da Ihre Objekte lange auf die Finalizer-Warteschlange warten können. Seien Sie nicht versucht, SuppressFinalize
andere 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 IDisposable
wird. 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, IDisposable
Ressourcen 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 IDisposable
Klassen hinzu, um zu testen, ob der Code ihr IDisposable
Objekt ordnungsgemäß entsorgt hat .
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
IDisposable
nicht sealed
der 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 .
sealed
Fü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
.
SupressFinalize
teilt 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.
SupressFinalize
Dies 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()
.
Diese Methode muss für die Dispose
Methode von Objekten aufgerufen werden, die das implementiert. Auf IDisposable
diese Weise würde der GC den Finalizer nicht ein anderes Mal aufrufen, wenn jemand die Dispose
Methode aufruft .
Siehe: GC.SuppressFinalize (Object) -Methode - Microsoft Docs
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.
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.