Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek se vztahuje na: ✔️ .NET Core 3.1 SDK a novější verze
Paměť může unikat, pokud vaše aplikace odkazuje na objekty, které už nepotřebuje k provedení požadované úlohy. Odkazování na tyto objekty brání uvolnění paměti využívané paměťovým správcem. To může vést ke snížení výkonu a OutOfMemoryException vyvolání výjimky.
Tento tutoriál demonstruje nástroje pro analýzu úniku paměti v aplikaci .NET pomocí nástrojů příkazového řádku .NET Diagnostics. Pokud používáte Windows, můžete k ladění úniku paměti použít diagnostické nástroje sady Visual Studio.
V tomto kurzu se jako cvičení používá ukázková aplikace, která záměrně nevracela paměť. Můžete také analyzovat aplikace, které neúmyslně způsobují únik paměti.
V tomto kurzu:
- Prozkoumejte využití spravované paměti pomocí dotnet-counters.
- Vygenerujte výpisový soubor.
- Analyzujte využití paměti pomocí souboru s výpisem paměti.
Požadavky
Tutoriál používá:
- Sada .NET Core 3.1 SDK nebo novější verze
- dotnet-counters k ověření využití spravované paměti.
- dotnet-dump ke shromažďování a analýze souboru s výpisem paměti (včetně rozšíření ladění SOS).
- Ukázková cílová aplikace pro ladění a diagnostiku.
Kurz předpokládá, že ukázkové aplikace a nástroje jsou nainstalované a připravené k použití.
Pokud vaše aplikace používá verzi .NET starší než .NET 9, bude výstupní uživatelské rozhraní čítačů dotnet-counter vypadat trochu jinak; Podrobnosti najdete v čítačích dotnet. .
Prozkoumání využití spravované paměti
Než začnete shromažďovat diagnostická data, která vám pomohou určit základní příčinu tohoto scénáře, ujistěte se, že skutečně dochází k úniku paměti (nárůst využití paměti). K potvrzení můžete použít nástroj dotnet-counters .
Otevřete okno příkazového řádku a přejděte do adresáře, do kterého jste stáhli a rozbalili ukázkový soubor ladění. Spusťte cíl:
dotnet run
V samostatné konzole vyhledejte ID procesu:
dotnet-counters ps
Výstup by měl vypadat přibližně takto:
4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
Poznámka:
Pokud předchozí příkaz nefunguje nebo ho nenajdete, budete pravděpodobně muset nástroj nejdřív nainstalovat dotnet-counters . Použijte následující příkaz:
dotnet tool install --global dotnet-counters
Teď pomocí nástroje dotnet-counters zkontrolujte využití spravované paměti. Určuje --refresh-interval počet sekund mezi aktualizacemi:
dotnet-counters monitor --refresh-interval 1 -p 4807
Živý výstup by měl vypadat přibližně takto:
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
Zaměřte se na tento řádek:
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
Můžete si všimnout, že paměť spravované haldy je 4 MB hned po spuštění.
Teď přejděte na adresu URL https://localhost:5001/api/diagscenario/memleak/20000.
Všimněte si, že využití paměti se zvýšilo na více než 20 MB.
dotnet.gc.last_collection.memory.committed_size (By) 21,020,672
Sledováním využití paměti můžete bezpečně říci, že paměť roste nebo uniká. Dalším krokem je shromáždit správná data pro analýzu paměti.
Generování výpisu paměti
Při analýze možných úniků paměti potřebujete přístup k haldě paměti aplikace, abyste mohli analyzovat její obsah. Když se podíváte na vztahy mezi objekty, vytvoříte teorie o tom, proč není paměť uvolněna. Běžným zdrojem diagnostických dat je výpis paměti na Windows nebo jeho ekvivalent na Linuxu. K vygenerování výpisu paměti aplikace .NET můžete použít nástroj dotnet-dump .
Spuštěním následujícího příkazu pomocí ukázkového cíle ladění vygenerujte výpis jádra Linuxu:
dotnet-dump collect -p 4807
Výsledkem je výpis paměti jádra umístěný ve stejné složce.
Writing minidump with heap to ./core_20190430_185145
Complete
Poznámka:
Pro porovnání v průběhu času nechte původní proces běžet i po shromáždění prvního výpisu a stejným způsobem shromážděte druhý výpis. Během určitého časového období byste pak měli dva výpisy paměti, které můžete porovnat, abyste zjistili, kde dochází ke zvětšování využití paměti.
Restartování neúspěšného procesu
Po shromáždění výpisu byste měli získat dostatek informací k diagnostice selhaného procesu. Pokud je proces selhání spuštěný na produkčním serveru, je teď ideální doba pro krátkodobou nápravu restartováním procesu.
V tomto kurzu jste teď hotovi s cílem ukázkového ladění a můžete ho zavřít. Přejděte do terminálu, který spustil server, a stiskněte Kombinaci kláves Ctrl+C.
Analýza výpisu paměti jádra
Teď, když máte vygenerovaný výpis paměti jádra, použijte nástroj dotnet-dump k analýze výpisu paměti:
dotnet-dump analyze core_20190430_185145
Kde core_20190430_185145 je název výpisu jádra, který chcete analyzovat.
Poznámka:
Pokud se zobrazí chyba, která si stěžuje, že libdl.so nebyla nalezena, možná budete muset nainstalovat balíček libc6-dev . Další informace najdete v tématu Požadavky pro .NET v Linuxu.
Zobrazí se výzva, kde můžete zadat příkazy SOS . První věc, na kterou se chcete podívat, je obvykle celkový stav spravované haldy:
> 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
Tady vidíte, že většina objektů je buď String nebo Customer objektů.
Příkaz můžete znovu použít dumpheap s tabulkou metod (MT), abyste získali seznam všech String instancí:
> 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
Teď můžete pomocí gcroot příkazu v System.String instanci zjistit, jak a proč je objekt root:
> 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.
Vidíte, že objekt String je přímo držen objektem Customer a nepřímo držen objektem CustomerCache.
Můžete pokračovat v odhazování objektů, abyste viděli, že většina String objektů dodržuje podobný vzor. V tomto okamžiku šetření poskytlo dostatečné informace k identifikaci původní příčiny v kódu.
Tento obecný postup umožňuje identifikovat zdroj zásadních úniků paměti.
Vyčistěte zdroje
V tomto kurzu jste spustili ukázkový webový server. Tento server by měl být vypnutý, jak je vysvětleno v části Restartování neúspěšného procesu .
Můžete také odstranit vytvořený soubor s výpisem paměti.
Viz také
- dotnet-trace k zobrazení procesů
- dotnet-counters ke kontrole využití řízené paměti
- dotnet-dump ke shromažďování a analýze souboru s výpisem paměti
- dotnet/diagnostics
- Pomocí sady Visual Studio ladit úniky paměti