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:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(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;}}publicvoidDispose()// 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 .
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
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().
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 .
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.
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.
Antworten:
SuppressFinalize
sollte nur von einer Klasse aufgerufen werden, die einen Finalizer hat. Es informiert den Garbage Collector (GC) darüber, dass dasthis
Objekt vollständig bereinigt wurde.Das empfohlene
IDisposable
Muster, wenn Sie einen Finalizer haben, ist: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 implementierenIDisposable
, 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 ihrIDisposable
Objekt ordnungsgemäß entsorgt hat .quelle
IDisposable
nichtsealed
derGC.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ützteDispose(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 ohneSuppressFinalize
.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:Im Allgemeinen sollte fast jede
Dispose()
Methode in der Lage sein, aufzurufenGC.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äß geschriebenerDispose()
/ Finalizer sollte ordnungsgemäß mit oder ohne Aufruf von funktionierenGC.SupressFinalize()
.quelle
Diese Methode muss für die
Dispose
Methode von Objekten aufgerufen werden, die das implementiert. AufIDisposable
diese Weise würde der GC den Finalizer nicht ein anderes Mal aufrufen, wenn jemand dieDispose
Methode aufruft .Siehe: GC.SuppressFinalize (Object) -Methode - Microsoft Docs
quelle
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.quelle
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)
oderGC.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()
undGC.SuppressFinalize(this)
sind in jeder Klasse, die keinen Finalizer hat, im Wesentlichen gleich, und Klassen, die Finalizer haben, sollten im Allgemeinen aufgerufenGC.SuppressFinalize(this)
werden. Daher ist die Verwendung der letzteren Funktion als letzter Schritt vonDispose()
möglicherweise nicht immer erforderlich, wird es aber nicht falsch liegen.quelle