Share via


Problemen bij hoog CPU-gebruik in .NET Core oplossen

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

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-belasting waarnemen. 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 waarden geven aan hoeveel seconden binnen de verversingsperiode (momenteel ingesteld op de standaardwaarde van Last Deltadotnet.process.cpu.time 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

Tijdens de aanvraag zal het CPU-gebruik rond de verhoogde waarde schommelen.

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

Wanneer u een app met een hoog CPU-gebruik analyseert, gebruikt u een profiler om te begrijpen wat de code doet. dotnet-trace collect werkt op alle besturingssystemen, maar safe-point bias en managed-only callstacks beperken deze tot meer algemene informatie dan een kernelbewuste profiler zoals ETW voor Windows of perf voor Linux. Afhankelijk van uw besturingssysteem en .NET-versie zijn er mogelijk verbeterde profileringsmogelijkheden beschikbaar. Zie de platformspecifieke tabbladen die volgen voor gedetailleerde richtlijnen.

Gebruik dotnet-trace collect-linux (.NET 10+)

Op .NET 10 en hoger dotnet-trace collect-linux is de aanbevolen profileringsbenadering in Linux. Het combineert EventPipe met perf_events op besturingssysteemniveau om één uniforme tracering te produceren die zowel beheerde als systeemeigen callstacks bevat, allemaal zonder dat een proces opnieuw hoeft te worden opgestart. Hiervoor zijn hoofdmachtigingen en Linux-kernel 6.4+ met CONFIG_USER_EVENTS=yvereist. Zie vereisten voor collect-linux voor volledige vereisten.

Zorg ervoor dat het voorbeeld debugdoel is geconfigureerd voor .NET 10 of hoger en voer vervolgens opnieuw het hoge CPU-eindpunt (https://localhost:5001/api/diagscenario/highcpu/60000) uit. Terwijl de aanvraag van 1 minuut loopt, voert u dotnet-trace collect-linux uit om een systeem-brede tracering vast te leggen.

sudo dotnet-trace collect-linux

Laat het ongeveer 20-30 seconden draaien en druk vervolgens op Ctrl+C of Enter om de verzameling te stoppen. Het resultaat is een .nettrace bestand dat zowel beheerde als systeemeigen callstacks bevat.

Open de .nettrace weergave met PerfViewcpu-stacks en gebruik deze om de methoden te identificeren die de meeste CPU-tijd verbruiken.

Zie Symbolen ophalen voor systeemeigen runtimeframes voor informatie over het oplossen van systeemeigen runtimesymbolen in de trace.

Gebruik perf

Het perf hulpprogramma kan ook worden gebruikt om .NET Core-app-profielen te genereren. Sluit het vorige exemplaar van het voorbeeld 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 door perf gebruikt om CPU-adressen op naam toe te wijzen aan door JIT gegenereerde functies. 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

Roep opnieuw het eindpunt van de hoge CPU-API aan (https://localhost:5001/api/diagscenario/highcpu/60000). Voer de perf opdracht uit met uw proces-id terwijl het binnen het verzoek van 1 minuut 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