Übung 2.1 Beheben von Leistungsproblemen mithilfe von createdump unter Linux

Gilt für:   .NET Core 2.1, .NET Core 3.1, .NET 5

Dieser Artikel hilft Ihnen bei der Analyse von Leistungsproblemen und beschreibt, wie Sie createdump und ProcDump verwenden, um .NET Core-Speicherabbilddateien manuell in Linux zu erfassen.

Voraussetzungen

Die Mindestanforderungen für die Einhaltung dieser Problembehandlungslabore sind wie folgt:

  • Eine ASP.NET Core-Anwendung, um Leistungsprobleme mit niedriger CPU- und hoher CPU-Leistung und Absturzprobleme zu veranschaulichen.
  • Der lldb-Debugger, der installiert und so konfiguriert ist, dass die SOS-Erweiterung geladen wird, wenn ein Kernabbild geöffnet wird.

Wenn Sie die vorherigen Teile dieser Reihe befolgt haben, sollten Sie das folgende Setup bereit haben:

  • Nginx ist so konfiguriert, dass zwei Websites gehostet werden:
    • Der erste lauscht auf Anforderungen mithilfe des hostheaders myfirstwebsite ( http://myfirstwebsite ) und Routinganforderungen an die Demo ASP.NET Core Anwendung, die auf Port 5000 lauscht.
    • Die zweite überwacht Anforderungen mithilfe des hostsamb-Hostheaders ( http://buggyamb ) und Routinganforderungen an die zweite ASP.NET Core Beispielanwendung, die auf Port 5001 lauscht.
  • Beide ASP.NET Core Anwendungen sollten als Dienste ausgeführt werden, die automatisch neu gestartet werden, wenn der Server neu gestartet wird oder die Anwendung nicht mehr reagiert.
  • Die lokale Linux-Firewall ist aktiviert und so konfiguriert, dass SSH- und HTTP-Datenverkehr zulässig ist.

Hinweis

Wenn Ihr Setup noch nicht bereit ist, wechseln Sie zu"Teil 2 Erstellen und Ausführen ASP.NET Core Apps".

Ziel dieser Übung

Bisher haben Sie in dieser Reihe zur Problembehandlung ein Absturzproblem analysiert. In dieser Übung erhalten Sie die Möglichkeit, ein Leistungsproblem zu analysieren und zu erfahren, wie Sie createdump und ProcDump verwenden, um Speicherabbilddateien manuell zu erfassen.

Reproduzieren des Problems

In einem vorherigen Teil haben Sie das erste "langsame" Szenario getestet, indem Sie die Slow-Verknüpfung ausgewählt haben. Wenn Sie dies tun, wird die Seite richtig geladen, aber viel langsamer als erwartet. In diesem Teil verwenden Sie das Feature "Auslastungs-Generator" von", um dieses Leistungsproblem zu beheben. Dies ist ein "experimentelles" Feature, das bis zu sechs gleichzeitige Anforderungen an eine problematische Ressource sendet. Sie ist auf sechs beschränkt, da jQuery- und Ajax-Aufrufe verwendet werden, um die Anforderungen auszugeben. Webbrowser legen einen Grenzwert für die meisten Ajax-Anforderungen auf sechs gleichzeitige Anforderungen für eine bestimmte URL fest. Wenn Sie erfahren möchten, wie Sie den Ladegenerator verwenden, um verschiedene Szenarien zu reproduzieren, lesen Sie den experimentellen "Ladegenerator".

Um das Problem zu reproduzieren, öffnen Sie Problemseiten, wählen Sie "Ladegenerator" aus, und senden Sie dann sechs Anforderungen im Slow-Szenario.

Problem mit Demamb.

Die folgende Liste zeigt, was Sie letztendlich in Ihrem Browser sehen sollten. Die angezeigten Antwortzeiten sind hoch. Die erwartete Antwortzeit beträgt weniger als eine Sekunde. Dies ist das, was Sie erwarten können, wenn Sie den Link "Erwartete Ergebnisse" auf der Startseite der Anwendung auswählen.

Die Auslastung von "DebuggeAmb".

Dies ist das Problem, das Sie beheben werden.

Überwachen der Symptome

Denken Sie daran, dass eine gute Problembehandlungssitzung beginnt, indem Sie das Problem definieren und die Symptome verstehen. Sie werden htop den Prozessspeicher und die CPU-Auslastung für den Prozess überwachen, der die ASP.NET Core Anwendung hostet, wenn Sie versuchen, das Problem durch Generieren von Last zu reproduzieren. Wenn Sie sich nicht merken, was htop ist, überprüfen Sie die vorherigen Serienteile.

Bevor Sie versuchen, das Problem zu reproduzieren, legen Sie zunächst einen Basisplan für die Leistung der Anwendung fest. Wählen Sie "Erwartete Ergebnisse" aus, oder senden Sie mehrere Anforderungen an das Szenario "Erwartete Ergebnisse", indem Sie das Feature "Ladegenerator" verwenden. Überprüfen Sie dann, wie die CPU- und Speicherauslastung aussieht, wenn sich das Problem nicht manifestieren kann. Sie werden htop die CPU- und Speicherauslastung überprüfen.

Führen Sie den Befehl aus, und filtern Sie htop ihn so, dass nur die Prozesse angezeigt werden, die dem Benutzer gehören, unter dem die Anwendung "run" ausgeführt wird. Der Benutzer des Ziel-ASP.NET Core-Anwendung ist in diesem Fall www-data. Drücken Sie die U-Taste, um den www-Datenbenutzer aus der Liste auszuwählen. Drücken Sie außerdem umschalt+H, um die Threads auszublenden. Wie Sie sehen können, werden vier Prozesse im Kontext von www-data ausgeführt, und zwei davon sind die Nginx-Prozesse. Die anderen sind für die Testanwendung und die Demoanwendung, die Sie beim Einrichten der Umgebung erstellt haben.

Da Sie das Leistungsproblem noch nicht reproduziert haben, beachten Sie, dass alle CPU- und Speicherauslastungsstatistiken derzeit niedrig sind.

Der Wert ist niedrig.

Kehren Sie nun zu Ihrem Clientbrowser zurück, und senden Sie sechs Anforderungen mithilfe des Ladegenerators an das Slow-Szenario. Kehren Sie danach schnell zu Ihrem Linux-Gerät zurück, und beobachten Sie den Ressourcenverbrauch des Prozesses in htop . Sie sollten sehen, dass die CPU-Auslastung der fehlerhaften Anwendung erheblich an anstieg und die Speicherauslastung nach oben und unten sinkt.

Arbeitsspeicher des Arbeitsspeichers" (1.0000 m2).

Hinweis

Da diese Ausgabe von einem virtuellen Computer stammt, der mit zwei logischen CPUs ausgestattet ist, htop wird eine CPU-Auslastung von mehr als 100 % angezeigt.

Nachdem alle Anforderungen schließlich verarbeitet wurden, nimmt die CPU- und Speicherauslastung ab. Sowohl cpu- als auch speicherauslastungstrends sollten Sie vermuten lassen, dass es während der Verarbeitung der Anforderungen zu einer hohen GC-Auslastung (Garbage Collector) in der Anwendung kommt.

Sammeln von Kernabbilddateien

Wenn Sie ein Leistungsproblem beheben, werden aufeinander folgende Speicherabbilddateien erfasst und analysiert. Die Idee für die Erfassung mehrerer Speicherabbilddateien ist einfach: Ein Prozessabbild ist eine Momentaufnahme des Prozessspeichers. Es enthält keine früheren Informationen. Um Leistungsprobleme zu beheben, sollten Sie mehrere manuelle Speicherabbilddateien oder Kernabbilddateien erfassen, damit Sie die Threads und Heaps vergleichen können usw.

Verwenden Sie die folgenden empfohlenen Optionen, um manuelle Speicherabbilddateien bei Bedarf zu erfassen:

  • Createdump
  • Procdump
  • Dotnet-Dump

Createdump

Createdump ist zusammen mit der .NET Core-Laufzeit enthalten. Er befindet sich im Laufzeitverzeichnis. Sie können die Laufzeitverzeichnispfade mithilfe des dotnet --list-runtimes Befehls suchen.

Runtimes.

Da es sich bei der Webanwendung um eine .NET Core 3.1-Anwendung handelt, lautet der vollständige Pfad von createdump /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.10/createdump .

Die einfachste Form dieses Befehls ist createdump <PID> . Dadurch wird ein Kernabbild für den Zielprozess geschrieben. Sie können das Tool angeben, in dem die Abbilddateien erstellt werden sollen, indem Sie den -f Schalter hinzufügen: createdump <PID> -f <filepath> . Erstellen Sie für diese Übung die Abbilddateien im ~/dumps/ Verzeichnis.

Sie erfassen zwei aufeinander folgende Speicherabbilddateien des "YoutubeAmb"-Prozesses 10 Sekunden auseinander. Sie müssen die Abbilddateien erfassen, während Sie das Problem "Langsam reagierende Anforderungen" reproduzieren. Um zu beginnen, müssen Sie zuerst die PID des Prozesses suchen. Verwenden Sie entweder den htop oder systemctl status buggyamb.service den Befehl. In den folgenden Listen ist die Prozess-PID 11724.

Führen Sie die folgenden Schritte aus, um die Abbilddateien zu erstellen:

  1. Erstellen Sie die erste Datei: sudo /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.10/createdump 11724 -f ~/dumps/coredump.manual.1.%d .
  2. Warten Sie 10 Sekunden, nachdem die erste Abbilddatei geschrieben wurde.
  3. Erstellen Sie die zweite Datei: sudo /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.10/createdump 11724 -f ~/dumps/coredump.manual.2.%d

Am Ende sollten Sie über zwei Speicherabbilddateien verfügen. Beachten Sie die Größe jeder Speicherabbilddatei.

GifAmb ll.

Analysieren von Abbilddateien in lldb

Sie sollten bereits wissen, wie Abbilddateien in lldb geöffnet werden. Öffnen Sie beide Dateien in lldb in zwei verschiedenen SSH-Sitzungen.

Ihr Ziel ist es, eine These darüber zu entwickeln, was das Leistungsproblem verursachen könnte. Sie wissen bereits, dass die CPU- und Arbeitsspeicherauslastung hoch ist, wenn das Problem auftritt. Um den verwalteten Speicher zu überprüfen, können Sie den dumpheap -stat Befehl verwenden. Bevor Sie beginnen, werfen Sie einen kurzen Blick auf die erste Abbilddatei.

Führen Sie den clrthreads Befehl aus, um eine Liste der verwalteten Threads abzurufen.

CsvAmb clrthreads.

Hinweis

Einer der Threads hat den GC-Modus auf "Kooperativ" festgelegt, die anderen auf "Preemptive".

Wenn der GC-Modus eines Threads auf "Preemptive" festgelegt ist, bedeutet dies, dass der GC diesen Thread jederzeit anhalten kann. Im Gegensatz dazu bedeutet der Kooperative Modus, dass der GC warten muss, bis der Thread in den Preemptive-Modus wechselt, bevor Sie ihn anhalten. Wenn der Thread verwalteten Code ausführt, befindet er sich im Kooperativen Modus.

Beginnen Sie, indem Sie den Thread im Kooperativen Modus untersuchen. Die Thread-ID des Debuggers für den kooperativen Thread ist 19 in der Beispielliste. Die ID unterscheidet sich, wenn Sie die Übung wiederholen. Wechseln Sie zum Thread, indem Sie ihn thread select 19 ausführen, und führen Sie ihn clrstack aus, um den verwalteten Aufrufstapel aufzuführen. Auf der "langsamen" Seite der Fehlerhaften Anwendung wird ein Zeichenfolgenverkettungsvorgang ausgeführt.

CsvAmb clrstack.

Dies macht Sie verdächtig, da Sie wissen sollten, dass Zeichenfolgenverkettungsvorgänge kostspielig sind. Dies liegt daran, dass Zeichenfolgenobjekte in .NET unveränderlich sind, was bedeutet, dass ihre Werte nicht geändert werden können, nachdem sie zugewiesen wurden. Betrachten Sie diesen Pseudocodeausschnitt:

string myText = "Debugging";
myText = myText + " .NET Core";
myText = myText + " is awesome";

Dieser Code erstellt mehrere Zeichenfolgen im Arbeitsspeicher: Debugging Debugging .NET Core , und Debugging .NET Core is awesome . Drei verschiedene Zeichenfolgenobjekte müssen erstellt werden, um eine letzte Zeichenfolge zu generieren (zu verketten). Wenn dies häufig genug auftritt, kann dies zu Speicherdruck führen, sodass die GC ausgelöst werden kann.

Diese These klingt unerhört. Sie sollten jedoch versuchen, zu überprüfen, ob es korrekt ist. Bevor Sie sich den verwalteten Heap ansehen und bereits im Threadkontext positioniert sind, überprüfen Sie die Objekte, auf die in diesem Thread verwiesen wird, um zu ermitteln, welche Zeichenfolgen- und Objektwerte vorhanden string[] sind. Führen Sie dso die Zeichenfolge aus, und konzentrieren Sie sich auf Zeichenfolgen- und Zeichenfolgenarrays.

CsvAmb dso.

Überprüfen Sie das Zeichenfolgenarray. Wird dumpobj mithilfe der Adresse des Objekts ausgeführt. Beachten Sie jedoch, dass dies nur veranschaulicht, dass es sich bei dem betreffenden Objekt um ein Array handelt. SOS stellt einen dumparray Befehl zum Untersuchen der Arrays bereit. Ausführen, dumparray 00007faf309528c8 um die Liste der Elemente im Array abzurufen. (Denken Sie daran, dass sich die Adresse des Arrayobjekts in der Speicherabbilddatei, die Sie untersuchen, unterscheidet.)

Dumpobj vom Typ &quot;Dumpobj&quot;.

Führen Sie den dumpobj Befehl erneut aus, indem Sie die resultierenden Zeichenfolgenadressen verwenden, die im Array enthalten sind. Wählen Sie einige der Adressen aus, und untersuchen Sie sie.

Die Dumpobj2-Datei &quot;DumpAmb&quot;.

Diese Zeichenfolgen ähneln den Zeichenfolgen in der Produkttabelle, die auf der Seite angezeigt wird.

Beachten Sie, dass lldb (oder SOS) den Zeichenfolgenwert möglicherweise nicht anzeigt, wenn die Zeichenfolgen groß sind. In solchen Fällen können Sie die systemeigenen Befehle von lldb verwenden, um die systemeigene Speicheradresse zu untersuchen. Dies ähnelt der Verwendung der d* Befehle (z. dc B. ) in WinDbg.

Der folgende Befehl liest den systemeigenen Speicher an der angegebenen Speicherposition und zeigt die ersten 384 Bytes an. Die Liste verwendet eine der Zeichenfolgenadressen, um dies zu veranschaulichen. Der Befehl, der ausgeführt wird, lautet memory read -c 384 00007fb14d5da040 .

Schreibzugriff auf Denamb.

Die Anzahl der Zeichenfolgen, auf die vom Stapel des Threads verwiesen wird, scheint die These zu bestätigen, dass das Problem der Zeichenfolgenverkettung das Leistungsproblem verursacht.

Die Untersuchung ist jedoch noch nicht abgeschlossen. Sie verfügen über zwei Speicherabbilddateien. Daher vergleichen Sie den Heap mit verwaltetem Speicher und überprüfen, wie sich der Heap rechtzeitig geändert hat.

Führen Sie den dumpheap -stat Befehl in jeder Speicherabbilddatei aus. Der folgende Code stammt aus der ersten Datei. In der liste unten sind 105.401 Zeichenfolgenobjekte vorhanden, und die Gesamtgröße der Zeichenfolgenobjekte beträgt ca. 480 MB. Beachten Sie außerdem, dass der Speicher möglicherweise fragmentiert ist und der Grund für die Fragmentierung mit den Zeichenfolgenarrayobjekten und System.Data.DataRow -objekten zusammenhängt.

CsvAmb dumpheap.

Fahren Sie fort, indem Sie dumpheap -stat denselben Befehl in der zweiten Abbilddatei ausführen. Sie sollten eine Änderung in den Fragmentierungsstatistiken sehen, aber dies ist im Zusammenhang mit dieser Untersuchung nicht wichtig. Der wichtige Teil ist die Anzahl der Zeichenfolgenobjekte und die erhebliche Größensteigerung dieser Objekte.

EnumerationAmb-Statistik.

Gleichzeitig nimmt auch die Anzahl der System.Data.DataRow Objekte zu.

You might suspect that there's an issue that involves a Large Object Heap ( BLOB). Daher sollten Sie die ENUMERATION-Objekte untersuchen. In diesem Fall sollten Sie die dumpheap -stat -min 85000 Befehle ausführen. Die folgende Liste enthält die Statistiken von XAML für das erste Speicherabbild.

Min. Min.

Und hier sind die Statistiken von XAML für das zweite Speicherabbild.

Min. Min.

Dies zeigt auch deutlich den Anstieg des Heaps. All dies scheint mit den Objekten in Zusammenhang zu string stehen.

Und was wäre, wenn Sie ein "live"-Objekt aus WIP auswählen würden, um dessen Stamm zu finden? "Live" bedeutet in diesem Fall, dass das Objekt an einer anderen Stelle verankert ist und daher von der Anwendung aktiv verwendet wird, sodass es vom GC-Prozess nicht gelöscht wird.

Der Umgang mit dieser Situation ist einfach. Führen Sie dumpheap -stat -min 85000 -live aus. Mit diesem Befehl werden nur Objekte angezeigt, die an einer anderen Stelle verankert sind. In diesem Beispiel gibt es nur die richtigen Instanzen von string Objekten, die in der CSV-Datei vorhanden sind.

CsvAmb live.

Verwenden Sie die MT-Adresse des string Objekts, um die Liste der Adressen dieser Liveobjekte abzurufen. Führen Sie dumpheap -mt 00007fb1602c0f90 -min 85000 -live aus.

CsvAmb live2.

Wählen Sie nun eine Adresse nach dem Zufallsprinzip aus der resultierenden Liste aus. Im folgenden Screenshot wird die dritte Adresse in der Liste angezeigt. Sie können versuchen, die ausgewählte Adresse zu überprüfen, indem Sie dumpobj . Da es sich jedoch um ein großes Objekt handelt, zeigt der Debugger den Wert nicht an. Überprüfen Sie stattdessen die systemeigene Speicheradresse ein weiteres Mal, und Sie werden sehen, dass es sich um ein string Objekt handelt, das dem ähnelt, was Sie in der Liste der Produkttabelle auf der Seite finden, die langsam reagiert.

Auf der Seite &quot;Qamb&quot;.

Untersuchen Sie den Stamm des objekts, das Sie aufgelistet haben. Verwenden Sie dazu den gcroot SOS-Befehl. Mit diesem Befehl wird einfach die Adresse des Objekts als Parameter in seiner einfachsten Form verwendet. Wie Sie sehen können, ist dies string auf den Thread gerootet, in dem die "langsame" Seite ausgeführt wird. Sie sollten sogar den Namen und die Zeilennummer der Quelldatei sehen.

CsvAmb gcroot.

Hinweis

Das Anzeigen des Quelldateinamens und der Zeilennummern hängt davon ab, wo Sie die Problembehandlung ausführen und ob Ihre Symbole richtig festgelegt sind. Im schlechtesten Fall können Sie zumindest die Thread-ID wiederherstellen. In der folgenden Liste ist b6c eine verwaltete Thread-ID. Wenn Sie clrthreads ausführen, finden Sie die entsprechende Thread-ID. BugsAmb b6c.

Wie im obigen Screenshot gezeigt, ist die Debuggerthread-ID für die verwaltete Thread-ID b6c 23. Wechseln Sie zu Thread 23, und überprüfen Sie den verwalteten Aufrufstapel. Wie Sie bereits gesehen haben, sollte dieser Thread auch Zeichenfolgenverkettungsvorgänge ausführen.

Der Thread &quot;Threads&quot; von &quot;Qamb&quot;.

Und wenn Sie den systemeigenen Aufrufstapel mithilfe des bt Befehls untersuchen, stellen Sie möglicherweise fest, dass die GC Speicher für diesen Thread zuteilen.

BugsAmb b6c.

Dieser Nachweis bestätigt die These, dass das Problem mit einer großen Anzahl von Zeichenfolgenverkettungsvorgängen zusammenhängt, die immer größere Zeichenfolgen erstellen, die während der Verarbeitung einer "langsamen" Seite ausgelöst werden.

Die Lösung für ein solches Problem liegt nicht im Rahmen dieser Reihe. Beachten Sie jedoch, dass die Lösung einfach mithilfe einer StringBuilder Klasseninstanz anstelle von Zeichenfolgenverkettungsvorgängen implementiert werden kann.

Nächste Schritte

Übung 2.2: Erfassen von Abbilddateien mit procDump