Ich verfolge ein Objekt mit WeakReference<T>
( kurze schwache Referenz ) in meiner Klasse Foo
. Diese Klasse hat einen Destruktor, auf den ich auf dieses verfolgte Objekt zugreifen muss. Das Objekt, das ich überwache, verfolgt auch Foo
mit WeakReference<Foo>
.
Nun frage ich mich, wann genau das "Nullstellen" der WeakReference
passiert? Werden alle WeakReference
vor der Ausführung eines Finalizers auf Null gesetzt, oder werden alle Nullen beendet, bevor der Finalizer des Objekts, das sie verfolgen, gerade ausgeführt wird?
AKTUALISIEREN
Jetzt frage ich mich auch, ob vielleicht Mono
project etwas Licht in dieses Thema bringen kann ( link 1 , link 2 ). Aber ich bin ein bisschen besorgt, dass MS GC
und Mono GC
dieses Problem möglicherweise anders behandeln und inkompatibel sein könnten.
Ich dachte daran, ein kleines Demo-Programm zu schreiben, das den Unterschied zeigt. Es stellte sich heraus, dass es etwas herausfordernder war, als ich es erwartet hatte. Die erste notwendige Zutat ist, sicherzustellen, dass der Finalizer-Thread verlangsamt werden kann, so dass Sie den Wert von WeakReference.IsAlive beobachten können, ohne zu riskieren, dass er von einem Rennen mit dem Finalizer-Thread beeinflusst wird. Also habe ich benutzt:
%Vor%Dann eine kleine Klasse, die Ziel der WeakReference sein wird:
%Vor%Dann ein Programm, das den Unterschied zwischen einer langen und einer kurzen schwachen Referenz zeigt:
%Vor%Sie müssen dieses Programm ausführen, damit der Debugger die Lebensdauer von Objekten nicht beeinflussen kann. Wählen Sie den Release-Build und ändern Sie eine Debugger-Einstellung: Extras + Optionen, Debugging, Allgemein, deaktivieren Sie die Option "JIT-Optimierung unterdrücken".
Es ist klar, dass die Finalisierungsreihenfolge der Objekte nicht deterministisch ist. Die Reihenfolge ist bei jedem Start des Programms anders. Wir möchten, dass das FinalizerDelayer-Objekt zuerst finalisiert wird, aber das passiert nicht immer. Ich denke es ist ein Nebeneffekt der integrierten Randomisierungsfunktion für das Layout des Adressraums, es macht verwalteten Code sehr schwer zu attackieren. Aber mach es oft genug und du wirst schließlich bekommen:
Delaying finalizer ...
Kurzlebig = falsch
Lang am Leben = Wahre Liebe Verzögerung erledigt
Beispiel 1 abgeschlossen
Beispiel 2 finalisiert Finalisierung erledigt
Lange am Leben = Falsch
Lange Rede kurzer Sinn:
Vorsicht vor einer Eigenart, wenn das Objekt wiederbelebt wird, wenn eine starke Referenz neu erstellt wird, wird es aus der freachable-Warteschlange in den normalen Heap versetzt. Nicht etwas, das ich in diesem Demo-Programm erkundet habe, aber eine lange, schwache Referenz wird benötigt, um das zu beobachten. Der Grund, warum Sie eine lange schwache Referenz benötigen.
Sie können das mit einem einfachen Testprogramm selbst überprüfen. Aber ich finde die Dokumentation für den WeakReference
-Typ selbst etwas klarer als die Seite, die Sie sich angesehen haben.
Insbesondere wird das Flag, das in Ihrer verknüpften Seite als "short" und "long" bezeichnet wird, trackResurrection
in die eigentliche Konstruktordokumentation . Und die Beschreibung für den Parameter lautet:
Gibt an, wann das Objekt gestoppt werden soll. Wenn dies der Fall ist, wird das Objekt nach der Fertigstellung verfolgt. Wenn false, wird das Objekt nur bis zur Fertigstellung verfolgt.
Der Abschnitt "Hinweise" lautet auch:
Wenn trackResurrection falsch ist, wird eine kurze schwache Referenz erstellt. Wenn trackResurrection true ist, wird eine lange schwache Referenz erstellt.
Dies bestätigt, dass wenn Sie eine "kurze" schwache Referenz verwenden, ein finalisiertes Objekt nicht mehr verfolgt wird (dh Target
wird null
) durch das WeakReference
Objekt, aber wenn Sie ein "long" verwenden schwache Referenz, wird es sein.
Bei beiden Arten der schwachen Referenz wird ein Objekt, das tatsächlich als Müll gesammelt wurde, natürlich nicht mehr verfolgt (offensichtlich).
Im Allgemeinen sollte kein anderer Thread in Ihrem Programm in der Lage sein, ein Objekt zu beobachten, während der Finalizer-Thread tatsächlich seine Arbeit verrichtet, also der genaue Moment für die "kurze" schwache Referenz, wenn die Target
-Eigenschaft auf% gesetzt ist co_de% scheint mir egal zu sein. Wenn ein anderer Thread in Ihrem Programm den Wert als nicht null betrachtet, wurde der Finalizer noch nicht ausgeführt. Wenn es als Null betrachtet wird, wurde der Finalizer ausgeführt. Dieser "andere Thread" sollte nicht selbst ausgeführt werden, während der Finalizer-Thread funktioniert, daher sollte die Finalisierung im Wesentlichen atomar sein, was den "anderen Thread" betrifft.
Nach vielen Nachforschungen konnte ich diesen alten Artikel Garbage Collection Teil 2: Automatic Memory Management finden im Microsoft .NET Framework ( Teil 1 diskutiert Auferstehung und frequeich wartbare Queue):
Nun, was passiert, wenn eine Garbage Collection (GC) ausgeführt wird:
Der Garbage Collector erstellt ein Diagramm aller erreichbaren Objekte. Teil 1 dieses Artikels erläutert, wie dies funktioniert.
Der Garbage Collector durchsucht die kurze schwache Referenztabelle. Wenn ein Zeiger in der Tabelle auf ein Objekt verweist, das nicht Teil des Diagramms ist, identifiziert der Zeiger ein nicht erreichbares Objekt und der Slot in die kurze schwache Referenztabelle wird auf null gesetzt .
Der Garbage Collector durchsucht die Finalisierungswarteschlange . Wenn sich ein Zeiger in der Warteschlange auf ein Objekt bezieht, das nicht Teil des Diagramms ist, identifiziert der Zeiger ein nicht erreichbares Objekt und der Zeiger wird von der Finalisierungswarteschlange in die Warteschlange für ausführbare Dateien verschoben. An dieser Stelle wird das Objekt dem Diagramm hinzugefügt, da das Objekt jetzt als erreichbar erachtet wird.
Der Garbage Collector durchsucht die lange schwache Referenztabelle. Wenn sich ein Zeiger in der Tabelle auf ein Objekt bezieht, das nicht Teil des Diagramms ist (das jetzt die Objekte enthält, auf die Einträge in der freachable-Warteschlange verweisen), identifiziert der Zeiger ein nicht erreichbares Objekt und der Slot wird auf null gesetzt.
Der Garbage Collector komprimiert den Speicher und drückt die Lücken aus, die die nicht erreichbaren Objekte hinterlassen haben.
Obwohl meine Klasse Foo
einen Finalizer hat und sich somit in einer Warteschlange befindet (die als Root betrachtet wird), geschieht das Auflösen von kurzen schwachen Referenzen, BEVOR dieses Objekt in der Freechable Queue verankert wird. was bedeutet, dass die kurze schwache Referenz null ist:
Garbage Collector setzt den Zeiger auf Null in der kurzen schwachen Referenztabelle, sobald festgestellt wurde, dass das Objekt nicht erreichbar ist. Wenn das Objekt über eine Finalize-Methode verfügt, wurde die Methode noch nicht aufgerufen, sodass das Objekt weiterhin existiert. Wenn die Anwendung auf die Target-Eigenschaft des WeakReference-Objekts zugreift, wird NULL zurückgegeben, obwohl das Objekt tatsächlich noch vorhanden ist.
Außerdem ist es, wie auf Yun Jins WebLog erwähnt Im Allgemeinen ist es nicht gut, finalisierbare Objekte in Finalizern zu referenzieren, aber WeakReference
ist ein bisschen eine Ausnahme (obwohl das war nicht immer der Fall ). Da WeakReference
ein Objekt mit Finalizer ist, wenn es im Finalizer von Foo
aufgerufen wurde, wurde es möglicherweise bereits finalisiert (in diesem Fall gibt die Eigenschaft Target
immer null zurück, obwohl das verfolgte Objekt möglicherweise noch immer vorhanden ist) lebendig und gut ( weitere Informationen ).
Ich habe gerade bestätigt, dass Mono Garbage Collector ist konsistent mit diesem Verhalten.
Nützliche Referenz:
- WeakReference Quellcode
- WeakReference & lt; T & gt; Quellcode
Tags und Links c# garbage-collection memory-management weak-references