2010-11-25 3 views
2

Ich habe einen Komponententest geschrieben, um zu bestätigen, dass das "Dispose" in meiner Klasse alle Ereignisse aushängt und einen Timer gibt, der auf das Objekt verweist.Gibt es eine Verzögerung nach einer vollständigen Speicherbereinigung, bevor WeakReference.IsLive() false wird?

Jedoch manchmal WeakReference.IsLive() gibt True zurück, wenn ich erwarten würde, dass es false zurückgibt?

Gibt es eine Verzögerung nach einem vollständigen GC bevor WeakReference.IsLive() aktualisiert wird?

Wenn nicht, können Sie an etwas anderes denken, das mir unwiederholbare Ergebnisse geben würde?

WeakReference weekJobWatchDog = new WeakReference(jobWatchDog); 
jobWatchDog = null; 

// not collected before Dispose called due to timer and events etc 
GC.Collect(); GC.Collect(); 
Assert.IsTrue(weekJobWatchDog.IsAlive); 

((IDisposable)weekJobWatchDog.Target).Dispose(); 

// is now collected as Dispose unlocked all events and dispoed the timer 
GC.Collect(); GC.Collect(); 
Assert.IsFalse(weekJobWatchDog.IsAlive); // sometimes this fails, about 1 in 4 runs 

Siehe auch Testing Finalizers and IDisposable für eine verwandte, aber andere Frage.

How can I write a unit test to determine whether an object can be garbage collected? hat eine soltuion die GC.WaitForPendingFinalizers(), aber ich lieber nicht nennen GC.WaitForPendingFinalizers(), wie ich will beweisen, dass meine dispose Werke und wenn es funktionierte es wird keine Notwendigkeit für irgendwelche Finalizers umfasst Aufruf ausführen .

+0

Haben Sie versucht, WaitForPendingFinalizers aufzurufen? – Henrik

+0

@Henrik, nein, da wir keine Finalizer haben wollen wir auf –

Antwort

0

Dies war das Problem, dass ich hatte:

Wenn Sie Dispose() aufrufen, auf einem System.Timers. Timer es kann zurückgeben, bevor der Win32-Timer zerstört wurde. Daher gibt es immer noch eine "Wurzel" im nicht verwalteten Raum, die den Timer am Leben erhält. Die Zeit hatte einen Event-Handler, der mein Objekt am Leben hielt.

Da dies sehr zeitnah verwandt ist, würde der Timer die meiste Zeit GCed bekommen und so würde mein Objekt. Aber manchmal (sagen wir 1 zu 10) würde der Timer am Leben bleiben und das würde auch mein Objekt sein.

Ein kurzer Sleep() würde meinen Test 100% der Zeit passieren lassen, also das Ereignis auf den Timer loslassen, bevor es entsorgt wird, so dass der Timer mein Objekt nicht am Leben halten kann.

siehe auch How do I safely dispose a System.Timers.Timer?

0

Da die Collect Methode nicht blockiert dann vermute ich - und das ist nur eine Vermutung -, dass der GC hat das Objekt nicht an dem Punkt gesammelt, die Sie IsAlive testen.

(Und vergessen Sie nicht, dass you can only trust IsAlive when it returns false.)

Ich nehme an, die Lösung haben könnte, wie WaitForPendingFinalizers ein blockierenden Aufruf zu etwas, auch wenn Sie Finalizers Ihrer eigenen haben nicht zu warten. (. Ich bin nicht sicher, ob es andere geeignete Blockierungs Methoden, die Sie stattdessen verwenden könnte)

+0

warten Ich dachte immer, dass die Collect-Methode standardmäßig blockiert, haben Sie eine Quelle, die besagt, dass es nicht blockiert? –

+1

@Ian: Ich denke du könntest recht haben. Ich kann keine definitive Dokumentation finden, um dies zu bestätigen. Der GC selbst sperrt nicht unbedingt alle App-Threads für die Dauer der gesamten Sammlung (es sei denn, Sie führen die nicht gleichzeitige Version des Workstation-GC aus), aber ich habe hier ein paar Tests durchgeführt, die darauf hindeuten, dass der Collect 'Methode * ist * blockiert. Vielleicht kann ein Experte mit konkreten Antworten aufwarten ... – LukeH

-2

Wenn die IsLive Eigenschaft eines WeakReference false zurückgibt, das heißt, die Referenz ist und wird für immer kaput werden, und es gibt keine Notwendigkeit, ihren Wert zu prüfen. Wenn es wahr zurückgibt, bedeutet das, dass die Referenz vielleicht noch am Leben ist, aber man wird es nicht wirklich wissen, bis man versucht, seinen Wert in eine starke Referenz zu fassen. Man sollte sich nicht darauf verlassen, dass eine WeakReference mit einem bestimmten Grad an Aktualität entwertet wird, noch sollte man ihren Wert annehmen, es sei denn, man ist tatsächlich daran interessiert. Wenn man beispielsweise eine Liste von WeakReferences aufräumt und die entfernten löscht, kann man mit der IsAlive-Eigenschaft diejenigen identifizieren, die wirklich tot sind, ohne starke Verweise auf solche zu erstellen, die ansonsten für die Garbage Collection geeignet wären.Es gibt keine besondere Garantie dafür, wann eine der schwachen Referenzen in der Liste für eine Bereinigung in Frage kommen würde, aber (1) eine solche Eignung wäre in Gegenwart von Speicherdruck aktueller; (2) In Ermangelung von Speicherdruck wäre Pünktlichkeit im Allgemeinen kein Problem.

Verwandte Themen