Dela via


Felsöka en minnesläcka i .NET

Den här artikeln gäller för: ✔️ .NET Core 3.1 SDK och senare versioner

Minnet kan läcka när appen refererar till objekt som den inte längre behöver utföra den önskade uppgiften. Om du refererar till dessa objekt hindras skräpinsamlaren från att frigöra det minne som används. Det kan leda till att prestandaförsämring och ett OutOfMemoryException undantag utlöses.

Den här självstudien visar verktygen för att analysera en minnesläcka i en .NET-app med hjälp av CLI-verktygen för .NET-diagnostik. Om du använder Windows kanske du kan använda Visual Studios verktyg för minnesdiagnostik för att felsöka minnesläckan.

I den här självstudien används en exempelapp som avsiktligt läcker minne som en övning. Du kan också analysera appar som oavsiktligt läcker minne.

I den här självstudien kommer vi att:

  • Granska hanterad minnesanvändning med dotnet-räknare.
  • Generera en dumpfil.
  • Analysera minnesanvändningen med hjälp av dumpfilen.

Förutsättningar

Självstudien använder:

Självstudien förutsätter att exempelapparna och verktygen är installerade och redo att användas.

Granska hanterad minnesanvändning

Innan du börjar samla in diagnostikdata för att hjälpa rotorsaken till det här scenariot måste du se till att du faktiskt ser en minnesläcka (ökad minnesanvändning). Du kan använda verktyget dotnet-counters för att bekräfta detta.

Öppna ett konsolfönster och navigera till katalogen där du laddade ned och packa upp felsökningsmålet. Kör målet:

dotnet run

Från en separat konsol hittar du process-ID:t:

dotnet-counters ps

Utdata bör likna:

4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios

Kontrollera nu den hanterade minnesanvändningen med verktyget dotnet-counters . --refresh-interval Anger antalet sekunder mellan uppdateringarna:

dotnet-counters monitor --refresh-interval 1 -p 4807

Liveutdata bör se ut ungefär så här:

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    # of Assemblies Loaded                           118
    % Time in GC (since last GC)                       0
    Allocation Rate (Bytes / sec)                 37,896
    CPU Usage (%)                                      0
    Exceptions / sec                                   0
    GC Heap Size (MB)                                  4
    Gen 0 GC / sec                                     0
    Gen 0 Size (B)                                     0
    Gen 1 GC / sec                                     0
    Gen 1 Size (B)                                     0
    Gen 2 GC / sec                                     0
    Gen 2 Size (B)                                     0
    LOH Size (B)                                       0
    Monitor Lock Contention Count / sec                0
    Number of Active Timers                            1
    ThreadPool Completed Work Items / sec             10
    ThreadPool Queue Length                            0
    ThreadPool Threads Count                           1
    Working Set (MB)                                  83

Fokusera på den här raden:

    GC Heap Size (MB)                                  4

Du kan se att det hanterade heapminnet är 4 MB direkt efter start.

Gå nu till URL:en https://localhost:5001/api/diagscenario/memleak/20000.

Observera att minnesanvändningen har ökat till 30 MB.

    GC Heap Size (MB)                                 30

Genom att titta på minnesanvändningen kan du på ett säkert sätt säga att minnet växer eller läcker. Nästa steg är att samla in rätt data för minnesanalys.

Generera minnesdump

När du analyserar möjliga minnesläckor behöver du åtkomst till appens minneshög för att analysera minnesinnehållet. När du tittar på relationer mellan objekt skapar du teorier om varför minnet inte frigörs. En vanlig diagnostikdatakälla är en minnesdump i Windows eller motsvarande kärndump i Linux. Om du vill generera en dump av ett .NET-program kan du använda verktyget dotnet-dump .

Kör följande kommando för att generera en Linux-kärndumpning med hjälp av exempelfelsökningsmålet som startades tidigare:

dotnet-dump collect -p 4807

Resultatet är en kärndump som finns i samma mapp.

Writing minidump with heap to ./core_20190430_185145
Complete

Kommentar

För en jämförelse över tid kan du låta den ursprungliga processen fortsätta köras efter insamling av den första dumpen och samla in en andra dump på samma sätt. Du skulle då ha två dumpar under en tidsperiod som du kan jämföra för att se var minnesanvändningen växer.

Starta om den misslyckade processen

När dumpen har samlats in bör du ha tillräckligt med information för att diagnostisera den misslyckade processen. Om den misslyckade processen körs på en produktionsserver är det nu den perfekta tiden för kortsiktig reparation genom att starta om processen.

I den här självstudien är du nu klar med felsökningsmålet Exempel och du kan stänga det. Gå till terminalen som startade servern och tryck på Ctrl+C.

Analysera kärndumpningen

Nu när du har genererat en kärndump använder du verktyget dotnet-dump för att analysera dumpen:

dotnet-dump analyze core_20190430_185145

Var core_20190430_185145 är namnet på den kärndump som du vill analysera.

Kommentar

Om du får ett felmeddelande om att libdl.so inte kan hittas kan du behöva installera libc6-dev-paketet . Mer information finns i Krav för .NET på Linux.

Du får en fråga där du kan ange SOS-kommandon . Vanligtvis är det första du vill titta på det övergripande tillståndet för den hanterade heapen:

> 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

Här kan du se att de flesta objekt är antingen String eller Customer objekt.

Du kan använda dumpheap kommandot igen med metodtabellen (MT) för att hämta en lista över alla String instanser:

> 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

Nu kan du använda gcroot kommandot på en System.String instans för att se hur och varför objektet är rotat:

> 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.

Du kan se att String är direkt innehas av objektet Customer och indirekt innehas av ett CustomerCache objekt.

Du kan fortsätta att dumpa objekt för att se att de flesta String objekt följer ett liknande mönster. Vid den här tidpunkten tillhandahöll undersökningen tillräckligt med information för att identifiera rotorsaken i koden.

Med den här allmänna proceduren kan du identifiera källan till större minnesläckor.

Rensa resurser

I den här självstudien startade du en exempelwebbserver. Den här servern bör ha stängts av enligt beskrivningen i avsnittet Starta om den misslyckade processen .

Du kan också ta bort dumpfilen som skapades.

Se även

Nästa steg