webentwicklung-frage-antwort-db.com.de

GC.Collect () und Finalize

Ok, es ist bekannt, dass GC implizit Finalize-Methoden für Objekte aufruft, wenn das Objekt als Müll identifiziert wird. Was passiert aber, wenn ich eine GC.Collect() mache? Sind die Finalisierer noch ausgeführt? Eine blöde Frage vielleicht, aber jemand fragte mich dies und ich antwortete mit "Ja" und dann dachte ich: " War das völlig korrekt? "

45
Sandeep

Ok, es ist bekannt, dass GC implizit Finalize-Methoden für Objekte aufruft, wenn es dieses Objekt als Garbage identifiziert.

Nein nein Nein. Das ist nicht bekannt , denn um Wissen zu haben, muss eine Aussage wahr sein ). Diese Aussage ist falsch . Der Garbage Collector führt beim Verfolgen keine Finalizer aus, ob er selbst ausgeführt wird oder ob Sie Collect aufrufen. Der Finalizer-Thread führt Finalizer aus, nachdem der Tracing-Collector den Garbage gefunden hat und das geschieht asynchron in Bezug auf einen Aufruf von Collect. (Wenn es überhaupt passiert, was möglicherweise nicht der Fall ist, wie eine andere Antwort zeigt.) Das heißt, Sie können sich nicht darauf verlassen, dass der Finalizer-Thread ausgeführt wird, bevor die Steuerung von Collect zurückkehrt.

Hier ist eine vereinfachte Skizze, wie es funktioniert:

  • Wenn eine Auflistung stattfindet, verfolgt der Garbage Collector-Ablaufverfolgungsthread die Wurzeln - die Objekte, von denen bekannt ist, dass sie lebendig sind, und jedes Objekt, auf das sie verweisen usw. -, um die toten Objekte zu bestimmen.
  • "Tote" Objekte mit ausstehenden Finalisierern werden in die Finalisiererwarteschlange verschoben. Die Finalizer-Warteschlange ist eine Wurzel. Daher sind diese "toten" Objekte tatsächlich noch am Leben .
  • Der Finalizer-Thread, der normalerweise ein anderer Thread als der GC-Trace-Thread ist, wird schließlich ausgeführt und leert die Finalizer-Warteschlange. Diese Objekte werden dann wirklich tot und werden in der nächsten Sammlung auf dem Ablaufverfolgungs-Thread gesammelt. (Da sie gerade die erste Sammlung überlebt haben, könnten sie in einer höheren Generation sein.)

Wie gesagt, das ist zu einfach; Die genauen Details zur Funktionsweise der Finalizer-Warteschlange sind etwas komplizierter. Aber es kommt genug von der Idee rüber. Das praktische Fazit ist, dass Sie nicht davon ausgehen können, dass der Aufruf von Collect auch Finalizer ausführt, da dies nicht der Fall ist. Lassen Sie mich das noch einmal wiederholen: der Tracing-Teil des Garbage Collectors führt keine Finalizer aus , und Collect führt nur den Tracing-Teil aus des Erfassungsmechanismus.

Rufen Sie nach dem Aufruf von WaitForPendingFinalizers das mit Collect bezeichnete Programm an, wenn Sie sicherstellen möchten, dass alle Finalizer ausgeführt wurden. Dadurch wird der aktuelle Thread angehalten, bis der Finalizer-Thread die Warteschlange geleert hat. Und wenn Sie sicherstellen möchten, dass der Speicher dieser finalisierten Objekte wiederhergestellt wird, müssen Sie Collect ein zweites Mal aufrufen.

Und natürlich sollten Sie dies nur zu Debug- und Testzwecken tun. Machen Sie diesen Unsinn im Produktionscode niemals ohne einen wirklich wirklich guten Grund.

75
Eric Lippert

Eigentlich die Antwort "Es kommt drauf an". Tatsächlich gibt es einen dedizierten Thread, der alle Finalizer ausführt. Das bedeutet, dass der Aufruf von GC.Collect nur diesen Prozess ausgelöst hat und die Ausführung aller Finalizer asynchron ausgeführt würde.

Wenn Sie warten möchten, bis alle Finalizer aufgerufen werden, können Sie den folgenden Trick verwenden:

GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();
15

Ja, aber nicht sofort. Dieser Auszug stammt aus Garbage Collection: Automatische Speicherverwaltung im Microsoft .NET Framework (MSDN Magazine) (*)

"Wenn eine Anwendung ein neues Objekt erstellt, reserviert der neue Operator den Arbeitsspeicher aus dem Heap. Wenn der Typ des Objekts eine Finalize-Methode enthält, wird ein Zeiger auf das Objekt in die Finalisierungswarteschlange gestellt. Die Finalisierungswarteschlange ist eine interne Datenstruktur, die vom Garbage Collector gesteuert wird. Jeder Eintrag in der Warteschlange zeigt auf ein Objekt, dessen Finalize-Methode aufgerufen werden muss, bevor der Speicher des Objekts aufgerufen wird.

Wenn ein GC auftritt, scannt der Speicherbereiniger die Finalisierung in der Warteschlange nach Zeigern auf diese Objekte suchen. Wenn ein Zeiger gefunden wird, Der Zeiger wird aus der Finalisierungswarteschlange entfernt und an die .__ angehängt. Freachable Queue (ausgesprochen "F-erreichbar"). Die fragwürdige Warteschlange ist eine andere interne Datenstruktur, die vom Garbage Collector gesteuert wird . Jeder Zeiger in der fraglichen Warteschlange identifiziert ein Objekt mit der Bezeichnung bereit, seine Finalize-Methode aufzurufen.

Es gibt einen speziellen Laufzeit-Thread für den Aufruf von Finalize Methoden. Wenn die fragliche Warteschlange leer ist (was normalerweise der Fall ist.), Schläft dieser Thread. Wenn Einträge angezeigt werden, weckt dieser Thread jedoch Entfernt jeden Eintrag aus der Warteschlange und ruft jedes Objekt Finalize .__ auf. Methode. Aus diesem Grund sollten Sie keinen Code in Finalize .__ ausführen. Eine Methode, die eine Annahme über den Thread macht, der die .__ ausführt. Code. Vermeiden Sie beispielsweise den Zugriff auf den lokalen Threadspeicher in Methode abschließen. "

(*) Ab November 2000 könnten sich die Dinge seitdem geändert haben.

10
stuartd

Wenn der Müll gesammelt wird (ob als Reaktion auf Speicherdruck oder GC.Collect()), werden die Objekte, die abgeschlossen werden müssen, in die Finalisierungswarteschlange gestellt.

Wenn Sie nicht GC.WaitForPendingFinalizers() aufrufen, werden die Finalizer möglicherweise lange nach dem Ende der Speicherbereinigung im Hintergrund ausgeführt.


Übrigens, es gibt keine Garantie, dass Finalizer überhaupt heißen. Von MSDN ...

Die Finalize-Methode wird möglicherweise nicht vollständig oder nicht unter .__ ausgeführt. alle in den folgenden außergewöhnlichen Umständen:

  • Ein anderer Finalizer blockiert auf unbestimmte Zeit (geht in eine Endlosschleife, versucht eine Sperre zu erhalten, die er niemals erreichen kann usw.). Weil der Laufzeit versucht, Finalizer vollständig auszuführen, andere Finalizer wird möglicherweise nicht aufgerufen, wenn ein Finalizer unbegrenzt blockiert.
  • Der Prozess wird beendet, ohne dass die Laufzeit eine Bereinigungsmöglichkeit hat. In diesem Fall wird die erste Benachrichtigung der Laufzeit über den Prozess Beendigung ist eine Benachrichtigung von DLL_PROCESS_DETACH.

Die Laufzeitumgebung schließt Objekte nur während des Herunterfahrens ab, während Die Anzahl der finalisierbaren Objekte nimmt weiter ab.

5

Einige weitere Punkte sind hier zu erwähnen.

Finalizer ist der letzte Punkt, an dem .net-Objekte nicht verwaltete Ressourcen freigeben können. Finalisierungen werden nur ausgeführt, wenn Sie Ihre Instanzen nicht ordnungsgemäß entsorgen. Idealerweise sollten Finalizer in vielen Fällen niemals ausgeführt werden. Denn die ordnungsgemäße Implementierung von dispose sollte die Finalisierung unterdrücken .

Hier ist ein Beispiel für eine korrekte IDispoable-Implementierung .

Wenn Sie die Dispose-Methode für Einwegobjekte aufrufen, sollten alle Verweise gelöscht und die Finalisierung unterdrückt werden. Wenn es nicht so gute Entwickler gibt, die vergessen, die Dispose-Methode aufzurufen, ist Finalizer der Lebensretter.

0
CharithJ