2009-08-12 12 views
6

Ich habe eine WindowsForms App, die Speicher zu verlieren scheint, also habe ich Redgate ANTS Memory Profiler verwendet, um die Objekte zu betrachten, die ich vermute, und sie nur von Objekten bereits auf der Finalizer-Warteschlange gehalten werden. Großartig, genau was ist eine Finalizer-Warteschlange? Kannst du mich auf die beste Definition hinweisen? Kannst du irgendeinen anekdotischen Rat geben?Was sind die Finalizer Queue und Control + ThreadMethodEntry?

Außerdem sind alle Root-GC-Objekte in der Finalizer-Warteschlange Instanzen von System.Windows.Forms.Control + ThreadMethodEntry Objekten namens "caller". Ich sehe, dass es in Multi-Thread-UI-Interaktion beteiligt ist, aber ich weiß nicht viel darüber hinaus. Verzeihen Sie meine scheinbare Faulheit und gestandene Ignoranz, aber diese Ressourcen sind alle in der Komponente eines Verkäufers vergraben. Ich spreche mit dem Verkäufer über diese Probleme, aber ich brauche eine Anleitung, um mich auf dem Laufenden zu halten. Können Sie mich auf die nützlichste Definition von ThreadMethodEntry hinweisen? Irgendwelche anekdotische Ratschläge?

Sollte ich mich auch über diese Objekte in der Finalizer-Warteschlange Sorgen machen?

Aktualisierung: Diese Red Gate article war hilfreich.

Antwort

14

Die Finalizer-Warteschlange enthält alle Objekte, für die eine Finalizer-Methode definiert wurde. Erinnern Sie sich, dass ein Finalizer ein Mittel zum Sammeln nicht verwalteter Ressourcen wie Handles ist. Wenn der Garbage Collector Müll sammelt, werden alle Objekte mit einem Finalizer in die Finalizer-Warteschlange verschoben. Irgendwann später - abhängig vom Speicherdruck, den GC-Heuristiken und der Phase des Mondes - wenn der Müllsammler beschließt, diese Objekte zu sammeln, geht er die Warteschlange entlang und führt die Finalizer aus.

Nachdem Sie in der Vergangenheit mit Speicherlecks gearbeitet haben, kann es passieren, dass die Objekte Ihres Anbieters in der Finalizer-Warteschlange einen schlampigen Code enthalten, jedoch kein Speicherleck anzeigen. In der Regel wird mit gutem Code eine Dispose-Methode bereitgestellt, die sowohl verwaltete als auch nicht verwaltete Ressourcen erfasst und sich dabei über GC.SuppressFinalize() aus der Finalizer-Warteschlange entfernt. Wenn also die Objekte des Herstellers eine Dispose-Methode implementieren und Ihr Code sie nicht aufruft, kann dies zu einer Reihe von Objekten in der Finalizer-Warteschlange führen.

Haben Sie versucht, einen Snapshot in ANTS zwischen zwei Zeitpunkten zu erstellen und die erstellten Objekte zu vergleichen? Dies kann Ihnen dabei helfen, verwaltete Objekte zu identifizieren, die ausgelaufen sind.

Auch, wenn Sie, wenn der Speicher sehen wollen, geht weg, wenn die Finalizers werden laufen, versuchen Sie dies nur zu testen, mit:

 
System.GC.Collect(); 
System.GC.WaitForPendingFinalizers(); // this method may block while it runs the finalizers 
System.GC.Collect(); 

Ich empfehle nicht, diesen Code normal läuft. Vielleicht möchten Sie es ausführen, wenn Sie gerade eine Menge Arbeit erledigt und viel Müll erstellt haben. In unserer App kann eine unserer Funktionen etwa 350 MB Müll erzeugen, der nach dem Schließen eines MDI-Fensters verschwendet wird. Da bekannt ist, dass viel Müll übrig bleibt, erzwingen wir manuell die Speicherbereinigung.

Beachten Sie auch, dass im Windows.Forms-Basiscode ein Low-Level-Eigenschaftencache vorhanden ist, der den zuletzt geöffneten modalen Dialog enthält. Dies könnte eine Quelle für ein Speicherleck sein. Eine sichere Möglichkeit, diese Referenz loszuwerden, besteht darin, einen weiteren einfachen Dialog zu erzwingen und den obigen GC-Code auszuführen.

+0

Danke für die tolle Antwort, Paul. Das ist das Objekt-Referenzdiagramm, von dem ich spreche, und sehe neue Objekte im zweiten Snapshot an, nachdem die Ressourcen bereinigt werden sollen. Alle Objekte im Diagramm, die IDisposable implementieren, haben eine QuickInfo, die besagt, dass "Dispose() für dieses Objekt aufgerufen wurde", aber das ausgewählte Objekt hat keine solche QuickInfo. – flipdoubt

+2

Hinweis zu ThreadMethodEntry: Ich denke, dass sie in jedem Invoke zum UI-Thread verwendet werden. Jedes Control-Objekt verfügt über eine Warteschlange mit Thread-Rückrufen vom Typ ThreadMethodEntry. Ein Rückruf entfernt einen ThreadMethodEntry und führt ihn aus. Jedes ThreadMethodEntry-Objekt hat eine Reihe interner Felder. Wenn Sie diese Felder untersuchen, können Sie möglicherweise herausfinden, welche Objekte dieses Lieferanten aufgerufen werden. Ich kann mich nicht erinnern, ob Sie diese Informationen von ANTS erhalten können, aber ich weiß, dass Sie über WinDbg.dll und sos.dll (verwaltete Debugger-Erweiterungen) können. Sehen Sie sich den Delegierten "method" und das Steuerelement "Anrufer" an. –

+0

Beachten Sie außerdem, dass die ThreadMethodEntry-Objekte einen Finalizer implementieren, jedoch keine Dispose-Methode. Wenn sie fertig sind, werden sie ebenfalls in die Finalizer-Warteschlange verschoben. –

1

Die Finalizer-Warteschlange ist eine Warteschlange, in der die Objektinstanzen, die nicht mehr verwendet werden, darauf warten, vom GC finalisiert zu werden. Alle Objekte in dieser Warteschlange werden finalisiert und Ihre Speicherlecks stammen wahrscheinlich nicht direkt von diesen. Eines dieser Objekte kann jedoch nicht alle nicht verwalteten Ressourcen freigeben.

Die ThreadMethodEntry-Klasse ist eine Implementierung von IAsyncResult, und Instanzen dieser Klasse werden normalerweise beim Aufrufen von asynchronen Vorgängen erstellt, z. B. mithilfe von Invoke zum Aktualisieren der Benutzeroberfläche oder mit Begin */End * -Methoden.

0

Here's ein guter Blogpost, der ein ähnliches Problem beschreibt. Auf einer eher technischen Ebene könnten Sie die Verwendung von SOS.dll (die im Blogpost beschrieben wird) und Sosex.dll verwenden, um herauszufinden, warum diese ThreadMethodEntry-Objekte im Speicher hängen bleiben. In diesen WinDbg-Erweiterungen gibt es Befehle, mit denen festgestellt werden kann, welche anderen Objekte auf ein bestimmtes Objekt im Speicher verweisen.