Delen via


Fouten opsporen in hoog CPU-gebruik in .NET Core

Dit artikel is van toepassing op: ✔️ .NET Core 3.1 SDK en latere versies

In deze zelfstudie leert u hoe u fouten kunt opsporen in een overmatig CPU-gebruiksscenario. Met behulp van het opgegeven voorbeeld ASP.NET broncodeopslagplaats voor core-web-apps , kunt u opzettelijk een impasse veroorzaken. Het eindpunt reageert niet meer en ondervindt accumulatie van threads. U leert hoe u verschillende hulpprogramma's kunt gebruiken om dit scenario te diagnosticeren met verschillende belangrijke onderdelen van diagnostische gegevens.

In deze zelfstudie leert u het volgende:

  • Hoog CPU-gebruik onderzoeken
  • CPU-gebruik bepalen met dotnet-tellers
  • Dotnet-trace gebruiken voor traceringsgeneratie
  • Profielprestaties in PerfView
  • Overmatig CPU-gebruik vaststellen en oplossen

Vereiste voorwaarden

In de zelfstudie wordt het volgende gebruikt:

CPU-meteritems

Voordat u deze zelfstudie probeert uit te voeren, installeert u de nieuwste versie van dotnet-tellers:

dotnet tool install --global dotnet-counters

Als in uw app een versie van .NET wordt uitgevoerd die ouder is dan .NET 9, ziet de uitvoerinterface van dotnet-counters er iets anders uit; zie dotnet-tellers voor meer informatie.

Voordat u diagnostische gegevens probeert te verzamelen, moet u een hoge CPU-voorwaarde observeren. Voer de voorbeeldtoepassing uit met behulp van de volgende opdracht uit de hoofdmap van het project.

dotnet run

Als u het huidige CPU-gebruik wilt controleren, gebruikt u de opdracht dotnet-counters tool:

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

De uitvoer moet er ongeveer als volgt uitzien:

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

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.109           0
        user                                                           0.453           0
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

Deze geven aan hoeveel seconden binnen de vernieuwingsperiode (momenteel ingesteld op Last Deltadotnet.process.cpu.timede standaardwaarde van 1 s) de CPU actief is geweest. Wanneer de web-app wordt uitgevoerd, onmiddellijk na het opstarten, wordt de CPU helemaal niet verbruikt en zijn deze verschillen beide 0. Navigeer naar de api/diagscenario/highcpu route met 60000 als de routeparameter:

https://localhost:5001/api/diagscenario/highcpu/60000

Voer nu de opdracht dotnet-counters opnieuw uit.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

U ziet een toename van het CPU-gebruik, zoals hieronder wordt weergegeven (afhankelijk van de hostcomputer, verwacht u een verschillend CPU-gebruik):

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

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.344           0.013
        user                                                          14.203           0.963
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

Gedurende de duur van de aanvraag beweegt het CPU-gebruik de muisaanwijzer rond de verhoogde waarde.

Aanbeveling

Als u een nog hoger CPU-gebruik wilt visualiseren, kunt u dit eindpunt tegelijkertijd in meerdere browsertabbladen uitvoeren.

Op dit moment kunt u veilig zeggen dat de CPU hoger is dan verwacht. Het identificeren van de gevolgen van een probleem is essentieel voor het vinden van de oorzaak. We gebruiken het effect van hoog CPU-verbruik naast diagnostische hulpprogramma's om de oorzaak van het probleem te vinden.

Hoge CPU analyseren met Profiler

Bij het analyseren van een app met een hoog CPU-gebruik hebt u een diagnostisch hulpprogramma nodig dat inzicht kan geven in wat de code doet. De gebruikelijke keuze is een profiler en er zijn verschillende profileropties waaruit u kunt kiezen. dotnet-trace kan worden gebruikt voor alle besturingssystemen, maar de beperkingen van safe-point bias en managed-only callstacks resulteren in meer algemene informatie vergeleken met een kernelbewuste profiler zoals 'perf' voor Linux of ETW voor Windows. Als uw prestatieonderzoek alleen beheerde code omvat, is over het algemeen dotnet-trace voldoende.

Het perf hulpprogramma kan worden gebruikt om .NET Core-app-profielen te genereren. We laten dit hulpprogramma zien, hoewel dotnet-trace ook kan worden gebruikt. Sluit het vorige exemplaar van het voorbeeld van het foutopsporingsdoel af.

Stel de DOTNET_PerfMapEnabled omgevingsvariabele in om ervoor te zorgen dat de .NET-app een map bestand in de /tmp map maakt. Dit map bestand wordt gebruikt om perf CPU-adressen toe te wijzen aan door JIT gegenereerde functies op naam. Zie Perf-kaarten en jit-dumps exporteren voor meer informatie.

Voer het voorbeelddoel voor foutopsporing uit in dezelfde terminalsessie.

export DOTNET_PerfMapEnabled=1
dotnet run

Oefen opnieuw het eindpunt van de HOGE CPU-API (https://localhost:5001/api/diagscenario/highcpu/60000). Voer de opdracht uit met uw perf proces-id terwijl deze binnen de aanvraag van 1 minuten wordt uitgevoerd:

sudo perf record -p 2266 -g

Met de perf opdracht wordt het proces voor het verzamelen van prestaties gestart. Laat het ongeveer 20-30 seconden draaien en druk vervolgens op Ctrl+C om het verzamelingsproces af te sluiten. U kunt dezelfde perf opdracht gebruiken om de uitvoer van de tracering te bekijken.

sudo perf report -f

U kunt ook een vlamgrafiek genereren met behulp van de volgende opdrachten:

git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

Met deze opdracht wordt een flamegraph.svg gegenereerd die u in de browser kunt bekijken om het prestatieprobleem te onderzoeken:

SVG-afbeelding van vlamgrafiek

Hoge CPU-gegevens analyseren met Visual Studio

Alle *.nettrace-bestanden kunnen worden geanalyseerd in Visual Studio. Als u een Linux *.nettrace-bestand in Visual Studio wilt analyseren, brengt u het *.nettrace-bestand, naast de andere benodigde documenten, over naar een Windows-computer en opent u vervolgens het bestand *.nettrace in Visual Studio. Zie Cpu-gebruiksgegevens analyseren voor meer informatie.

Zie ook

Volgende stappen