Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo si applica a: ✔️ .NET Core 3.1 SDK e versioni successive
La memoria può verificarsi un leak quando l'app fa riferimento a oggetti di cui non ha più bisogno per eseguire l'attività desiderata. Facendo riferimento a questi oggetti si impedisce al Garbage Collector di recuperare la memoria usata. Ciò può comportare una riduzione delle prestazioni e viene generata un'eccezione OutOfMemoryException.
Questa esercitazione illustra gli strumenti per analizzare una perdita di memoria in un'app .NET usando gli strumenti dell'interfaccia della riga di comando di diagnostica .NET. Se si usa Windows, è possibile usare gli strumenti di diagnostica della memoria di Visual Studio per eseguire il debug della perdita di memoria.
Questa esercitazione utilizza un'app di esempio che perde intenzionalmente memoria, come parte dell'esercizio. È anche possibile analizzare le app che causano involontariamente perdite di memoria.
In questa esercitazione si eseguiranno le seguenti attività:
- Esaminare l'utilizzo della memoria gestita con dotnet-counters.
- Generare un file di dump.
- Analizzare l'utilizzo della memoria usando il file dump.
Prerequisiti
Il tutorial utilizza:
- .NET Core 3.1 SDK o versione successiva.
- dotnet-counters per controllare l'utilizzo della memoria gestita.
- dotnet-dump per raccogliere e analizzare un file di dump (include l'estensione di debug SOS).
- Un'app di esempio di debug target da diagnosticare.
L'esercitazione presuppone che le app e gli strumenti di esempio siano installati e pronti per l'uso.
Se l'app esegue una versione di .NET precedente a .NET 9, l'interfaccia utente di output dei contatori dotnet sarà leggermente diversa; per informazioni dettagliate, vedere dotnet-counters .
Esaminare l'utilizzo della memoria gestita
Prima di iniziare a raccogliere i dati di diagnostica per individuare la causa principale di questo scenario, assicurarsi di aver effettivamente individuato un leak di memoria (ovvero un aumento dell'uso della memoria). È possibile usare lo strumento dotnet-counters per confermarlo.
Aprire una finestra della console e passare alla directory in cui è stata scaricata e decompressa la destinazione di debug di esempio. Esegui il target
dotnet run
Da una console separata, trova l'ID del processo.
dotnet-counters ps
L'output dovrebbe essere simile a:
4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
Annotazioni
Se il comando precedente non funziona o non viene trovato, è probabile che sia necessario installare prima lo dotnet-counters strumento. Utilizza il seguente comando:
dotnet tool install --global dotnet-counters
Controllare ora l'utilizzo della memoria gestita con lo strumento dotnet-counters .
--refresh-interval Specifica il numero di secondi tra gli aggiornamenti:
dotnet-counters monitor --refresh-interval 1 -p 4807
L'output live dovrebbe essere simile al seguente:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 111
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 1
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 4,431,712
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 803,576
gen1 15,456
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 811,960
gen1 1,214,720
gen2 0
loh 0
poh 24,528
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
dotnet.gc.pause.time (s) 0.003
dotnet.jit.compilation.time (s) 0.329
dotnet.jit.compiled_il.size (By) 120,212
dotnet.jit.compiled_methods ({method}) 1,202
dotnet.monitor.lock_contentions ({contention}) 2
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.344
user 0.344
dotnet.process.memory.working_set (By) 64,331,776
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 7
dotnet.timer.count ({timer}) 0
Concentrarsi su questa riga:
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
È possibile notare che la memoria dell'heap gestita è di 4 MB subito dopo l'avvio.
Passare ora all'URL https://localhost:5001/api/diagscenario/memleak/20000.
Osservare che l'utilizzo della memoria è cresciuto fino a oltre 20 MB.
dotnet.gc.last_collection.memory.committed_size (By) 21,020,672
Osservando l'utilizzo della memoria, è possibile dire in modo sicuro che la memoria è in crescita o perdita. Il passaggio successivo consiste nel raccogliere i dati corretti per l'analisi della memoria.
Generare il dump della memoria
Quando si analizzano possibili perdite di memoria, è necessario accedere all'heap di memoria dell'app per analizzare il contenuto della memoria. Esaminando le relazioni tra oggetti, si creano teorie sul motivo per cui la memoria non viene liberata. Un'origine dati di diagnostica comune è un dump della memoria su Windows o un core dump equivalente su Linux. Per generare un dump di un'applicazione .NET, è possibile usare lo strumento dotnet-dump .
Usando la destinazione di debug di esempio avviata in precedenza, eseguire il comando seguente per generare un core dump di Linux:
dotnet-dump collect -p 4807
Il risultato è un dump principale che si trova nella stessa cartella.
Writing minidump with heap to ./core_20190430_185145
Complete
Annotazioni
Per un confronto nel tempo, lasciare che il processo originale continui a funzionare dopo la raccolta del primo dump e raccogliere un secondo dump nello stesso modo. Si avranno quindi due dump in un periodo di tempo che è possibile confrontare per vedere dove cresce l'utilizzo della memoria.
Riavviare il processo non riuscito
Dopo aver raccolto il dump, è necessario disporre di informazioni sufficienti per diagnosticare il processo non riuscito. Se il processo non riuscito è in esecuzione in un server di produzione, è il momento ideale per la correzione a breve termine riavviando il processo.
In questa esercitazione è stato completato l'esempio di destinazione di debug, ed è possibile chiuderla. Passare al terminale che ha avviato il server e premere CTRL+C.
Analizzare il core dump
Ora che è stato generato un dump principale, usare lo strumento dotnet-dump per analizzare il dump:
dotnet-dump analyze core_20190430_185145
Dove core_20190430_185145 è il nome del dump principale da analizzare.
Annotazioni
Se viene visualizzato un errore che segnala che non è possibile trovare libdl.so , potrebbe essere necessario installare il pacchetto libc6-dev . Per altre informazioni, vedere Prerequisiti per .NET in Linux.
Verrà visualizzato un prompt in cui è possibile immettere i comandi SOS . In genere, la prima cosa da esaminare è lo stato complessivo dell'heap gestito:
> 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
Qui è possibile vedere che la maggior parte degli oggetti è String o Customer oggetti.
È possibile usare di nuovo il dumpheap comando con la tabella del metodo (MT) per ottenere un elenco di tutte le String istanze:
> 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
È ora possibile usare il comando gcroot su un'istanza System.String per vedere come e perché l'oggetto è radicato.
> 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.
È possibile notare che l'oggetto String è tenuto direttamente dall'oggetto Customer e indirettamente mantenuto da un CustomerCache oggetto .
È possibile continuare a eseguire il dump degli oggetti per verificare che la maggior parte String degli oggetti segua un modello simile. A questo punto, l'indagine ha fornito informazioni sufficienti per identificare la causa principale nel tuo codice.
Questa procedura generale consente di identificare l'origine di perdite di memoria principali.
Pulire le risorse
In questa esercitazione è stato avviato un server Web di esempio. Questo server sarebbe dovuto essere stato arrestato come spiegato nella sezione Riavviare il processo non riuscito.
È anche possibile eliminare il file di dump creato.
Vedere anche
- dotnet-trace per elencare i processi
- dotnet-counters per controllare l'utilizzo della memoria gestita
- dotnet-dump per raccogliere e analizzare un file di dump
- dotnet/diagnostics
- Usare Visual Studio per eseguire il debug delle perdite di memoria