Freigeben über


Debuggen von StackOverflow-Fehlern

Ein StackOverflowException wird ausgelöst, wenn der Ausführungsstapel überläuft, da er zu viele geschachtelte Methodenaufrufe enthält. Dies tritt sehr häufig auf, da Methoden rekursivaufrufen.

Angenommen, Sie haben eine App wie folgt:

using System;

namespace temp
{
    class Program
    {
        static void Main(string[] args)
        {
            Main(args); // Oops, this recursion won't stop.
        }
    }
}

Die Main-Methode ruft sich kontinuierlich auf, bis kein Stapelspeicher mehr vorhanden ist. Sobald kein Stapelspeicher mehr vorhanden ist, kann die Ausführung nicht fortgesetzt werden, sodass ein StackOverflowExceptionausgelöst wird.

> dotnet run
Stack overflow.
   at temp.Program.Main(System.String[])
   at temp.Program.Main(System.String[])
   at temp.Program.Main(System.String[])
   at temp.Program.Main(System.String[])
   at temp.Program.Main(System.String[])
   at temp.Program.Main(System.String[])
   <this output repeats many more times>

Wenn das Programm mit der Ausgabe wie folgt beendet wird, können Sie den Quellcode für die wiederholten Methoden finden und die Logik untersuchen, die die große Anzahl von Aufrufen verursacht.

Verwenden des Debuggers

Häufig genügt es, diesen Aufrufstack auf der obigen Konsole zu sehen, um den problematischen Code zu identifizieren. Wenn das Problem jedoch noch unklar ist, können Sie das Problem weiter debuggen.

In diesem Beispiel wird ein Kernabbild erstellt, wenn die StackOverflowException auftritt, anschließend wird das Abbild in lldb (ein gängiger Linux-Befehlszeilendebugger) geladen und gedebuggt.

  1. Führen Sie die App mit der Konfiguration zum Generieren eines Absturzabbilds aus.

    > export DOTNET_DbgEnableMiniDump=1
    > dotnet run
    Stack overflow.
    Writing minidump with heap to file /tmp/coredump.6412
    Written 58191872 bytes (14207 pages) to core file
    

    Anmerkung

    .NET 6 standardisiert das Präfix DOTNET_ anstelle von COMPlus_ für Umgebungsvariablen, die das .NET-Laufzeitverhalten konfigurieren. Das Präfix COMPlus_ funktioniert jedoch weiterhin. Wenn Sie eine frühere Version der .NET-Laufzeit verwenden, sollten Sie weiterhin das präfix COMPlus_ für Umgebungsvariablen verwenden.

  2. Installieren Sie die SOS-Erweiterung mithilfe von dotnet-sos.

    dotnet-sos install
    
  3. Öffnen Sie den Dump in lldb und verwenden Sie den Befehl bt (Rückverfolgung), um den Stack anzuzeigen.

    lldb --core /temp/coredump.6412
    (lldb) bt
    ...
        frame #261930: 0x00007f59b40900cc
        frame #261931: 0x00007f59b40900cc
        frame #261932: 0x00007f59b40900cc
        frame #261933: 0x00007f59b40900cc
        frame #261934: 0x00007f59b40900cc
        frame #261935: 0x00007f5a2d4a080f libcoreclr.so`CallDescrWorkerInternal at unixasmmacrosamd64.inc:867
        frame #261936: 0x00007f5a2d3cc4c3 libcoreclr.so`MethodDescCallSite::CallTargetWorker(unsigned long const*, unsigned long*, int) at callhelpers.cpp:70
        frame #261937: 0x00007f5a2d3cc468 libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=<unavailable>, pArguments=0x00007ffe8222e7b0, pReturnValue=0x0000000000000000, cbReturnValue=0) at callhelpers.cpp:604
        frame #261938: 0x00007f5a2d4b6182 libcoreclr.so`RunMain(MethodDesc*, short, int*, PtrArray**) [inlined] MethodDescCallSite::Call(this=<unavailable>, pArguments=<unavailable>) at callhelpers.h:468
    ...
    
  4. Der obere Rahmen 0x00007f59b40900cc wird mehrmals wiederholt. Verwenden Sie den Befehl SOS-ip2md, um herauszufinden, welche verwaltete Methode sich an der 0x00007f59b40900cc Adresse befindet.

    (lldb) ip2md 0x00007f59b40900cc
    MethodDesc:   00007f59b413ffa8
    Method Name:          temp.Program.Main(System.String[])
    Class:                00007f59b4181d40
    MethodTable:          00007f59b4190020
    mdToken:              0000000006000001
    Module:               00007f59b413dbf8
    IsJitted:             yes
    Current CodeAddr:     00007f59b40900a0
    Version History:
      ILCodeVersion:      0000000000000000
      ReJIT ID:           0
      IL Addr:            0000000000000000
         CodeAddr:           00007f59b40900a0  (MinOptJitted)
         NativeCodeVersion:  0000000000000000
    Source file:  /temp/Program.cs @ 9
    
  5. Schauen Sie sich die angegebene Methode temp an. Program.Main(System.String[]) und quelle "/temp/Program.cs @ 9", um festzustellen, ob Sie herausfinden können, was der Code falsch macht. Wenn zusätzliche Informationen erforderlich sind, können Sie weitere Debugger- oder SOS--Befehle verwenden, um den Prozess zu prüfen.