Redgate logo for print use

ANTS Memory ProfilerANTS Memory Profiler

Schritt-für-Schritt-Anleitung: So spüren Sie ein Speicherleck in einer WinForms-Anwendung mit dem ANTS Memory Profiler auf

Diese Anleitung zeigt, wie Sie mit dem ANTS Memory Profiler ein Speicherleck aufspüren, anhand der Beispielanwendung QueryBee. QueryBee ist eine einfache WinForms-Anwendung, mit der Sie SQL Server-Datenbanken abfragen können. Sie besteht aus einem Verbindungsdialog und einem Abfragefenster. Wir wissen, dass unsere Anwendung Speicher verliert, denn jedes Mal, wenn wir das Abfragefenster öffnen und wieder schließen, steigt die Speichernutzung.

Nach dem Start des Profilers sehen Sie zunächst den Startbildschirm:

Abbildung 1: Der Startbildschirm des ANTS Memory Profilers.
Abbildung 1: Der Startbildschirm des ANTS Memory Profilers.

Hier sehen Sie eine Liste der zuletzt durchgeführten Profiler-Sitzungen. So können Sie diese leicht erneut starten. Für dieses Beispiel beginnen wir eine neue Sitzung über „New profiling session“.

Der Bildschirm „New profiling session“ wird angezeigt:

Abbildung 2: Neue Profiler-Sitzung schnell und einfach konfigurieren.
Abbildung 2: Neue Profiler-Sitzung schnell und einfach konfigurieren.

Jetzt müssen Sie nur noch QueryBee auswählen, gewünschte Performance Counter einstellen und auf „Start profiling“ klicken.

Der Profiler startet QueryBee und beginnt mit der Datenerfassung:

Abbildung 3: Während der Analyse erfasst der Profiler Leistungsdaten. Zudem erhalten Sie Hinweise, wann Snapshots zu nehmen und zu vergleichen sind.
Abbildung 3: Während der Analyse erfasst der Profiler Leistungsdaten. Zudem erhalten Sie Hinweise, wann Snapshots zu nehmen und zu vergleichen sind.

Das Aufnehmen und Vergleichen von Speichersnapshots ist zentral beim Aufspüren von Speicherlecks. Unser Vorgehen lautet daher:

  1. Warten, bis QueryBee geöffnet ist.
  2. Ersten Snapshot aufnehmen, ohne die Anwendung zu nutzen, denn dieser dient als Referenzwert.
  3. In QueryBee die vermutete fehlerverursachende Aktion ausführen.
  4. Zweiten Snapshot aufnehmen.
  5. Snapshot-Vergleich analysieren und Hinweise auswerten.

QueryBee ist jetzt geöffnet und befindet sich im Benachrichtigungsfeld.

Wir nehmen nun den ersten Snapshot auf, um später vergleichen zu können.

Beim Klick auf „Take Memory Snapshot“ erzwingt der Profiler eine vollständige Speicherbereinigung und erfasst den aktuellen Heap-Speicherzustand.

Abbildung 4: Zusammenfassung des ersten Snapshots.
Abbildung 4: Zusammenfassung des ersten Snapshots.

Anschließend führen wir in QueryBee die Aktionen aus, die wir für das Speicherleck verantwortlich machen.

Wir öffnen QueryBee und stellen eine Verbindung zur Datenbank her.

Abbildung 5: QueryBee – Der Datenbank-Verbindungsdialog.
Abbildung 5: QueryBee – Der Datenbank-Verbindungsdialog.
Abbildung 6: QueryBee – Das Abfragefenster.
Abbildung 6: QueryBee – Das Abfragefenster.

Das Abfragefenster öffnet sich, wir geben eine SQL-Abfrage ein und führen sie aus.

Es erscheinen Ergebnisse, danach schließen wir das Abfragefenster.

Abbildung 7: QueryBee – Ergebnisse werden im Grid angezeigt.
Abbildung 7: QueryBee – Ergebnisse werden im Grid angezeigt.

Das Abfrageformular wird geschlossen.

Das Fenster ist nun zwar geschlossen, aber die Speichernutzung sinkt nicht wie erwartet.

Abbildung 8: Trotz geschlossenem Fenster bleibt die Speichernutzung hoch.
Abbildung 8: Trotz geschlossenem Fenster bleibt die Speichernutzung hoch.

Was passiert hier? Wir nehmen einen zweiten Snapshot auf und lassen ihn analysieren.

Abbildung 9: Die Zusammenfassung vergleicht beide Snapshots.
Abbildung 9: Die Zusammenfassung vergleicht beide Snapshots.

Die Zusammenfassung hebt mehrere Probleme hervor:

Wir könnten nun eine der großen Klassen aus der Übersicht auswählen, wechseln aber zur Klassenliste für eine detailliertere Analyse.

Uns interessieren Objekte, die seit dem ersten Snapshot neu erstellt wurden. Wir sortieren die Liste daher absteigend nach dem Unterschied in der Instanzanzahl (Instance Diff).

Class list
Abbildung 10: Die Klassenliste erlaubt einen detaillierten Vergleich der Snapshots.

Ganz oben steht die Klasse „String“ – mit über 300.000 neuen Instanzen. Um zu verstehen, warum, öffnen wir den Instanz-Kategorisierer über das Symbol für das Instance-Retention-Diagramm.

Abbildung 11: Der Kategorisierer zeigt Instanzen nach ihrer kürzesten Referenzkette zur GC-Wurzel.
Abbildung 11: Der Kategorisierer zeigt Instanzen nach ihrer kürzesten Referenzkette zur GC-Wurzel.

Über 21 MB an String-Objekten werden via QueryForm und ConnectForm über denselben Pfad im Speicher gehalten. Wir klicken auf „Show the Instances on this Path“, um die Instanzen aufzulisten.

Abbildung 12: Die Instanzliste zeigt uns Strings aus der SQL-Datenbank.
Abbildung 12: Die Instanzliste zeigt uns Strings aus der SQL-Datenbank.

Die Liste enthält Daten, die QueryBee aus der Datenbank geladen hat, diese hätten beim Schließen des QueryForms jedoch entfernt werden müssen. Wir wählen eine Instanz aus und klicken erneut auf das Symbol für das Retention-Diagramm.

Abbildung 13: Retention-Diagramm für die ausgewählte Instanz.
Abbildung 13: Retention-Diagramm für die ausgewählte Instanz.

Im Diagramm sehen wir, welche Referenzkette die Objekte im Speicher hält. Sobald wir die Kette an einem Punkt unterbrechen, kann der Garbage Collector alles darunterliegende freigeben.

Wir beginnen unten im Diagramm und arbeiten uns nach oben, bis wir die verantwortliche Referenz finden.

Die Strings werden offenbar von der QueryForm gehalten, obwohl diese schon hätte entfernt sein sollen. Noch weiter oben im Diagramm sehen wir, dass ein EventHandler die QueryForm referenziert, und dieser wiederum wird von der ConnectForm gehalten (Eingabemaske für die Verbindungsdaten).

Ein näherer Blick zeigt: Die Referenz geschieht über das Feld „Foregrounded“ der ConnectForm.

Wir suchen das Foregrounded-Ereignis im Code, klicken mit rechts auf den Knoten „QueryBee.ConnectForm“ und öffnen den Quellcode in Visual Studio™.

Abbildung 14: Foregrounded-Ereignis im Quellcode der ConnectForm.
Abbildung 14: Foregrounded-Ereignis im Quellcode der ConnectForm.

Der Profiler springt automatisch zur Definition. Über „Find All References“ sehen wir, dass das Event an drei Stellen verwendet wird.

Abbildung 15: Das Foregrounded-Ereignis wird an drei Stellen verwendet.
Abbildung 15: Das Foregrounded-Ereignis wird an drei Stellen verwendet.

Die letzte Referenz zeigt: Die QueryForm registriert sich für das Event, entfernt sich aber nicht wieder. Wenn wir das beheben, sollte das Speicherleck verschwinden.

Zuvor müssen wir das Profiling beenden, damit die Anwendung neu gebaut werden kann, also klicken wir auf „Stop Profiling“.

Dann bauen wir QueryBee neu.

Abbildung 16: Die Anwendung QueryBee wird neu gebaut.
Abbildung 16: Die Anwendung QueryBee wird neu gebaut.

Zurück im Profiler starten wir eine neue Sitzung, diesmal mit dem Ziel, sicherzustellen, dass QueryForm nicht mehr im Speicher verbleibt.

Die Einstellungen von vorher wurden übernommen. Wir klicken nur noch auf „Start Profiling“.

Abbildung 17: Die Sitzungsdialog merkt sich die vorherigen Einstellungen.
Abbildung 17: Die Sitzungsdialog merkt sich die vorherigen Einstellungen.

Wir nehmen erneut einen ersten Snapshot zur Referenz auf.

Dann führen wir – wie zuvor – die gleichen Aktionen durch: Baseline-Snapshot, Abfrage durchführen, zweiten Snapshot.

Zusätzlich nehmen wir einen dritten Snapshot auf, um sicherzustellen, dass QueryForm auch wirklich verschwunden ist.

Nach dem Schließen des Abfragefensters mit dem Ergebnis-Grid nehmen wir den dritten Snapshot.

Wir vergleichen nun Snapshot 1 und 3 über die Auswahl oberhalb der Zeitleiste.

Abbildung 18: Vergleichsansicht zwischen Snapshot 1 und 3.
Abbildung 18: Vergleichsansicht zwischen Snapshot 1 und 3.

Die Speichernutzung ist jetzt nur noch minimal gestiegen, was ein gutes Zeichen ist. Zeit, nachzusehen, ob es noch eine QueryForm gibt.

Wir filtern die Klassenliste auf den Namespace QueryBee.

Abbildung 19: Klassenliste für den Namespace QueryBee.
Abbildung 19: Klassenliste für den Namespace QueryBee.

Nein – QueryForm ist verschwunden. Das Speicherleck wurde behoben.

Wie Sie sehen, lässt sich ein speicherfressendes Formular mit dem ANTS Memory Profiler recht schnell aufspüren.


Warum laden Sie den ANTS Memory Profiler nicht herunter, testen ihn an Ihrer Anwendung und sehen selbst, wie leicht sich Speicherlecks damit finden lassen?