Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel gilt für: ✔️ .NET Core 3.1 SDK und höher
Speicher kann verloren gehen, wenn Ihre App auf Objekte verweist, die nicht mehr die gewünschte Aufgabe ausführen müssen. Durch Verweisen auf diese Objekte wird verhindert, dass der Garbage Collector den verwendeten Arbeitsspeicher zurückgibt. Dies kann zu Leistungsbeeinträchtigungen und zum Auslösen einer OutOfMemoryException-Ausnahme führen.
In diesem Lernprogramm werden die Tools zum Analysieren eines Speicherverlusts in einer .NET-App mithilfe der .NET Diagnostics CLI-Tools veranschaulicht. Wenn Sie Windows verwenden, können Sie möglicherweise die Speicherdiagnosetools von Visual Studio verwenden , um den Speicherverlust zu debuggen.
In diesem Tutorial wird als Übung eine Beispiel-App verwendet, bei der absichtlich Arbeitsspeicher verloren geht. Sie können auch Apps analysieren, die unbeabsichtigt Speicherlecks verursachen.
In diesem Tutorial werden Sie Folgendes lernen:
- Untersuchen Sie den verwalteten Speicherverbrauch mit dotnet-counters.
- Generieren einer Dumpdatei.
- Analysieren Sie die Speicherauslastung mithilfe der Speicherabbilddatei.
Voraussetzungen
Das Tutorial verwendet:
- .NET Core 3.1 SDK oder eine höhere Version.
- dotnet-counters, um den verwalteten Speicherverbrauch zu überprüfen.
- dotnet-dump zum Sammeln und Analysieren einer Dumpdatei (einschließlich der SOS-Debugerweiterung).
- Eine Beispiel-Debugziel-App zur Diagnose.
Im Lernprogramm wird davon ausgegangen, dass die Beispiel-Apps und -Tools installiert und einsatzbereit sind.
Wenn Ihre App eine version von .NET ausführt, die älter als .NET 9 ist, sieht die Ausgabebenutzeroberfläche von dotnet-counters etwas anders aus. Weitere Informationen finden Sie unter dotnet-counters .
Überprüfen der Verwalteten Speicherauslastung
Bevor Sie mit dem Erfassen von Diagnosedaten beginnen, um die Grundursache für dieses Szenario zu ermitteln, stellen Sie sicher, dass tatsächlich ein Arbeitsspeicherverlust stattfindet (Steigerung der Arbeitsspeichernutzung). Sie können das Tool dotnet-counters verwenden, um dies zu bestätigen.
Öffnen Sie ein Konsolenfenster, und navigieren Sie zu dem Verzeichnis, in das Sie das Beispieldebugziel heruntergeladen und entzippt haben. Führen Sie das Ziel aus:
dotnet run
Suchen Sie in einer separaten Konsole die Prozess-ID:
dotnet-counters ps
Die Ausgabe sollte in etwa wie folgt aussehen:
4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
Hinweis
Wenn der vorherige Befehl nicht funktioniert oder nicht gefunden wird, müssen Sie das dotnet-counters Tool wahrscheinlich zuerst installieren. Verwenden Sie den folgenden Befehl:
dotnet tool install --global dotnet-counters
Überprüfen Sie nun die verwaltete Speicherauslastung mit dem Dotnet-Counter-Tool . Dies --refresh-interval gibt die Anzahl der Sekunden zwischen Aktualisierungen an:
dotnet-counters monitor --refresh-interval 1 -p 4807
Die Liveausgabe sollte ähnlich wie die folgenden sein:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 111
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 1
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 4,431,712
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 803,576
gen1 15,456
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 811,960
gen1 1,214,720
gen2 0
loh 0
poh 24,528
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
dotnet.gc.pause.time (s) 0.003
dotnet.jit.compilation.time (s) 0.329
dotnet.jit.compiled_il.size (By) 120,212
dotnet.jit.compiled_methods ({method}) 1,202
dotnet.monitor.lock_contentions ({contention}) 2
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.344
user 0.344
dotnet.process.memory.working_set (By) 64,331,776
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 7
dotnet.timer.count ({timer}) 0
Konzentrieren Sie sich auf diese Zeile:
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
Sie können sehen, dass der verwaltete Heap-Speicher direkt nach dem Start 4 MB beträgt.
Wechseln Sie nun zur URL https://localhost:5001/api/diagscenario/memleak/20000.
Beachten Sie, dass die Speicherauslastung auf über 20 MB gestiegen ist.
dotnet.gc.last_collection.memory.committed_size (By) 21,020,672
Indem Sie die Speicherauslastung beobachten, können Sie sicher sagen, dass der Speicher wächst oder verloren geht. Der nächste Schritt besteht darin, die richtigen Daten für die Speicheranalyse zu sammeln.
Generieren eines Speicherabbilds
Bei der Analyse möglicher Speicherverluste benötigen Sie Zugriff auf den Speicher-Heap der App, um den Speicherinhalt zu analysieren. Wenn Sie beziehungen zwischen Objekten betrachten, erstellen Sie Theorien darüber, warum der Speicher nicht freigegeben wird. Eine allgemeine Diagnosedatenquelle ist ein Speicherabbild unter Windows oder das entsprechende Kernabbild unter Linux. Zum Generieren eines Dumps einer . NET-Anwendung können Sie das dotnet-dump-Tool verwenden.
Führen Sie mit dem zuvor gestarteten Beispiel-Debug-Ziel den folgenden Befehl aus, um einen Linux-Core-Dump zu erstellen.
dotnet-dump collect -p 4807
Das Ergebnis ist ein Core Dump, der sich im gleichen Ordner befindet.
Writing minidump with heap to ./core_20190430_185145
Complete
Hinweis
Lassen Sie für einen Vergleich im Laufe der Zeit den ursprünglichen Prozess nach dem Sammeln des ersten Speicherabbilds weiter ausführen, und sammeln Sie ein zweites Speicherabbild auf die gleiche Weise. Sie würden dann über zwei Speicherabbilder über einen Zeitraum verfügen, die Sie vergleichen können, um zu sehen, wo die Speicherauslastung wächst.
Neustart des fehlgeschlagenen Prozesses
Nachdem der Dump erfasst ist, sollten Sie über ausreichende Informationen verfügen, um den fehlgeschlagenen Prozess zu diagnostizieren. Wenn der fehlgeschlagene Prozess auf einem Produktionsserver ausgeführt wird, ist es jetzt die ideale Zeit für kurzfristige Korrekturen, indem der Prozess neu gestartet wird.
In diesem Tutorial benötigen Sie das Beispieldebugziel nicht mehr, und Sie können es schließen. Navigieren Sie zum Terminal, das den Server gestartet hat, und drücken Sie STRG+C.
Analysieren des Speicherabbilds
Nachdem Sie nun ein Speicherabbild generiert haben, verwenden Sie das Tool dotnet-dump, um das Speicherabbild zu analysieren:
dotnet-dump analyze core_20190430_185145
Dabei ist core_20190430_185145 der Name des Speicherabbilds, das Sie analysieren möchten.
Hinweis
Wenn ein Fehler angezeigt wird, der beschwert, dass libdl.so nicht gefunden werden kann, müssen Sie möglicherweise das libc6-Dev-Paket installieren. Weitere Informationen finden Sie unter Voraussetzungen für .NET unter Linux.
Sie erhalten eine Eingabeaufforderung, an der Sie SOS-Befehle eingeben können. Im Allgemeinen gilt die erste Untersuchung dem Gesamtstatus des verwalteten Heaps:
> dumpheap -stat
Statistics:
MT Count TotalSize Class Name
...
00007f6c1eeefba8 576 59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8 1749 95696 System.SByte[]
00000000008c9db0 3847 116080 Free
00007f6c1e784a18 175 128640 System.Char[]
00007f6c1dbf5510 217 133504 System.Object[]
00007f6c1dc014c0 467 416464 System.Byte[]
00007f6c21625038 6 4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498 200000 4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90 206770 19494060 System.String
Total 428516 objects
Hier können Sie sehen, dass die meisten Objekte entweder String oder Customer Objekte sind.
Sie können den dumpheap Befehl erneut mit der Methodentabelle (MT) verwenden, um eine Liste aller String Instanzen abzurufen:
> dumpheap -mt 00007f6c1dc00f90
Address MT Size
...
00007f6ad09421f8 00007faddaa50f90 94
...
00007f6ad0965b20 00007f6c1dc00f90 80
00007f6ad0965c10 00007f6c1dc00f90 80
00007f6ad0965d00 00007f6c1dc00f90 80
00007f6ad0965df0 00007f6c1dc00f90 80
00007f6ad0965ee0 00007f6c1dc00f90 80
Statistics:
MT Count TotalSize Class Name
00007f6c1dc00f90 206770 19494060 System.String
Total 206770 objects
Sie können nun den gcroot Befehl für eine System.String Instanz verwenden, um zu sehen, wie und warum das Objekt verwurzelt ist:
> gcroot 00007f6ad09421f8
Thread 3f68:
00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues() [/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
rbx: (interior)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
HandleTable:
00007F6C98BB15F8 (pinned handle)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
Found 2 roots.
Man sieht, dass das String-Objekt direkt vom Customer-Objekt gehalten wird und indirekt von einem CustomerCache-Objekt.
Sie können weiterhin Objekte verwerfen, um zu sehen, dass die meisten String Objekte einem ähnlichen Muster folgen. An diesem Punkt lieferte die Untersuchung ausreichende Informationen, um die Ursache in Ihrem Code zu identifizieren.
Mit diesem allgemeinen Verfahren können Sie die Quelle wichtiger Speicherverluste identifizieren.
Bereinigen von Ressourcen
In diesem Lernprogramm haben Sie einen Beispielwebserver gestartet. Dieser Server sollte heruntergefahren worden sein, wie im Abschnitt " Fehler beim Prozess neu starten " erläutert wurde.
Sie können auch die erstellte Dumpdatei löschen.
Siehe auch
- dotnet-trace, um Prozesse aufzulisten
- dotnet-counters, um die verwaltete Speichernutzung zu überprüfen
- dotnet-dump zum Sammeln und Analysieren einer Dumpdatei
- dotnet/diagnostics
- Verwenden von Visual Studio zum Debuggen von Speicherlecks