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 popisuje problémy související s uvolňováním paměti a využitím paměti. Řeší problémy týkající se spravované haldy a vysvětluje, jak minimalizovat dopad uvolňování paměti na vaše aplikace. Každý problém obsahuje odkazy na postupy, které můžete použít ke zkoumání problémů.
Nástroje pro analýzu výkonu
Následující části popisují nástroje, které jsou k dispozici pro zkoumání využití paměti a problémů s garbage collection. Postupy uvedené dále v tomto článku se týkají těchto nástrojů.
Čítače výkonu paměti
Ke shromažďování dat o výkonu můžete použít čítače výkonu. Pokyny najdete v tématu Profilace modulu runtime. Kategorie čítačů výkonu pro paměť .NET CLR, jak je uvedeno v části Čítače výkonu v .NET, poskytuje informace o garbage collectoru.
Ladění pomocí SOS
Ladicí program systému Windows (WinDbg) můžete použít ke kontrole objektů ve spravované haldě.
Chcete-li nainstalovat WinDbg, nainstalujte nástroje ladění pro Windows ze stránky Stáhnout nástroje ladění pro Windows .
Události Trasování událostí pro Windows uvolňování paměti
Trasování událostí pro Windows (ETW) je systém trasování, který doplňuje podporu profilace a ladění poskytované rozhraním .NET. Počínaje rozhraním .NET Framework 4 zachytává události tras uvolňování paměti pro Windows užitečné informace pro analýzu spravované haldy ze statistického hlediska. Například se událost GCStart_V1
, která se vyvolá, když se chystá nastat uvolňování paměti, poskytuje následující informace:
- Která generace objektů se shromažďuje.
- Co aktivovalo uvolňování paměti.
- Typ uvolňování paměti (souběžné nebo ne souběžné).
Protokolování událostí pomocí ETW je efektivní a nezamaskuje žádné problémy s výkonem spojené s uvolňováním paměti. Proces může poskytovat své vlastní události spolu s událostmi ETW. Při zaznamenávání lze propojovat události aplikace i události uvolňování paměti, abychom určili, jak a kdy dochází k problémům s haldou. Serverová aplikace může například poskytovat události na začátku a na konci požadavku klienta.
Rozhraní API pro profilaci
Profilovací rozhraní modulů CLR (Společné jazykové moduly) poskytují podrobné informace o objektech ovlivněných během uvolňování paměti. Profiler může být upozorněn, když začne a skončí uvolňování paměti. Může poskytovat zprávy o objektech v dané spravované haldě, včetně identifikace objektů v každé generaci. Další informace najdete v tématu Přehled profilace.
Profilátoři můžou poskytovat komplexní informace. Složité profilátory ale můžou potenciálně změnit chování aplikace.
Monitorování zdrojů v aplikační doméně
Počínaje rozhraním .NET Framework 4 umožňuje monitorování prostředků domény aplikace (ARM) hostitelům monitorovat využití procesoru a paměti podle domény aplikace. Další informace najdete v tématu Monitorování prostředků domény aplikace.
Řešení potíží s výkonem
Prvním krokem je určit, zda problém skutečně spočívá ve správě paměti. Pokud zjistíte, že se jedná o problém, vyberte z následujícího seznamu pro jeho řešení.
- Vyvolá se výjimka z nedostatku paměti
- Proces používá příliš mnoho paměti.
- Sběrač odpadu nevrací objekty dostatečně rychle.
- Spravovaná halda je příliš fragmentovaná.
- Pauzy při uvolňování paměti jsou příliš dlouhé
- Generace 0 je příliš velká
- Využití procesoru při uvolňování paměti je příliš vysoké
Problém: Byla vyvolána výjimka nedostatku paměti
Existují dva legitimní případy, kdy může dojít k vyvolání spravovaného OutOfMemoryException objektu:
Dochází k virtuální paměti.
Správce paměti (garbage collector) přiděluje paměť ze systému v segmentech předem určené velikosti. Pokud přidělení vyžaduje další segment, ale v prostoru virtuální paměti procesu není žádný souvislý volný blok, přidělení spravované haldy se nezdaří.
Nemá dostatek fyzické paměti k dispozici.
Kontroly výkonu |
---|
Určete, jestli je výjimka vyčerpání paměti řízena. Určete, kolik virtuální paměti je možné rezervovat. Určete, jestli je dostatek fyzické paměti. |
Pokud zjistíte, že výjimka není legitimní, obraťte se na zákaznickou službu a podporu Společnosti Microsoft s následujícími informacemi:
- Zásobník s výjimkou vyčerpání paměti ve spravovaném kontextu.
- Úplný výpis paměti.
- Data, která ukazují, že se nejedná o legitimní výjimku mimo paměť, včetně dat, která ukazují, že virtuální nebo fyzická paměť není problém.
Problém: Proces používá příliš mnoho paměti
Běžným předpokladem je, že zobrazení využití paměti na kartě Výkon ve Správci úloh systému Windows může znamenat, kdy se používá příliš mnoho paměti. Toto zobrazení se však týká pracovní sady; neposkytuje informace o využití virtuální paměti.
Pokud zjistíte, že příčinou problému je spravovaná halda, je nutné změřit spravovanou haldu v průběhu času a určit případné vzory.
Pokud zjistíte, že příčinou problému není spravovaná halda, musíte použít nativní ladění.
Problém: Garbage Collector nezískává objekty zpět dostatečně rychle
Pokud se zdá, že objekty nejsou uvolněny podle očekávání při automatickém uvolňování paměti, je nutné určit, zda existují silné odkazy na ty objekty.
K tomuto problému může dojít také v případě, že nedošlo k uvolnění paměti pro generování, které obsahuje mrtvý objekt, což značí, že finalizátor mrtvého objektu nebyl spuštěn. To je například možné, když používáte aplikaci STA (Single-Threaded Apartment) a vlákno, které obsluhuje frontu finalizátoru nemůže do ní volat.
Kontroly výkonu |
---|
Zkontrolujte odkazy na objekty. Určete, jestli byl finalizátor spuštěn. Určete, jestli existují objekty, které čekají na dokončení. |
Problém: Spravovaná halda je příliš fragmentovaná
Úroveň fragmentace se vypočítá jako poměr volného místa nad celkovou přidělenou pamětí pro generování. Pro generaci 2 není přijatelná úroveň fragmentace větší než 20%. Vzhledem k tomu, že generace 2 může být velmi velká, je poměr fragmentace důležitější než absolutní hodnota.
Když máte ve generaci 0 hodně volného místa, není problém, protože se jedná o generaci, ve které jsou přidělovány nové objekty.
Fragmentace se vždy vyskytuje v haldě velkého objektu, protože není komprimovaná. Volné objekty, které sousedí, jsou přirozeně sloučeny do jednoho prostoru, aby vyhovovaly velkým požadavkům na přidělování objektů.
Fragmentace může být problémem generace 1 a generace 2. Pokud mají tyto generace po uvolňování paměti velké množství volného místa, může být potřeba upravit využití objektů aplikace a měli byste zvážit opětovné vyhodnocení životnosti dlouhodobých objektů.
Nadměrné připnutí objektů může zvýšit fragmentaci. Pokud je fragmentace vysoká, mohlo být připnuto příliš mnoho objektů.
Pokud fragmentace virtuální paměti brání garbage collectoru v přidávání segmentů, může to být jedna z následujících příčin:
Časté nakládání a vykládání mnoha malých sestavení.
Při spolupráci s nespravovaným kódem držíte příliš mnoho odkazů na objekty COM.
Vytváření velkých dočasných objektů, což způsobuje, že halda velkých objektů často přiděluje a uvolňuje segmenty haldy.
Při hostování CLR může aplikace požádat, aby garbage collector zachoval své segmenty. Tím se sníží frekvence přidělování segmentů. Toho dosáhnete pomocí příznaku STARTUP_HOARD_GC_VM v STARTUP_FLAGS výčtu.
Kontroly výkonu |
---|
Určete množství volného místa ve spravované haldě. Určete počet připnutých objektů. |
Pokud si myslíte, že nedochází k žádné oprávněné příčině fragmentace, obraťte se na zákaznickou službu Microsoftu a podporu.
Problém: Pozastavení uvolňování paměti je příliš dlouhé
Správa paměti probíhá v rámci měkkého reálného času, takže aplikace musí být schopná tolerovat některé pauzy. Kritériem pro měkký reálný čas je, že 95% operací musí být dokončeno včas.
Při souběžném sběru odpadu mohou spravovaná vlákna běžet během shromažďování, což znamená, že přerušení jsou velmi minimální.
Dočasné uvolňování paměti (generace 0 a 1) trvá jen několik milisekund, takže snížení pozastavení obvykle není možné. Pozastavení v kolekcích druhé generace ale můžete snížit změnou vzoru žádostí o přidělení prostředků aplikace.
Další, přesnější metodou je použití ETW událostí pro sledování garbage kolekce. Časování kolekcí najdete přidáním rozdílů časových razítek pro posloupnost událostí. Celá sekvence kolekce zahrnuje pozastavení výkonného modulu, samotný sběr paměti a obnovení výkonného modulu.
Pomocí oznámení o uvolňování paměti můžete určit, jestli se server chystá mít kolekci generace 2 a jestli přesměrování požadavků na jiný server může usnadnit jakékoli problémy s pozastavením.
Kontroly výkonu |
---|
Určete délku trvání v rámci procesu uvolňování paměti. Určete, co způsobilo uvolňování paměti. |
Problém: Generace 0 je příliš velká
Generace 0 pravděpodobně bude mít větší počet objektů v 64bitovém systému, zejména pokud místo uvolňování paměti pro pracovní stanici používáte uvolňování paměti pro server. Důvodem je, že prahová hodnota pro spuštění uvolňování paměti generace 0 je v těchto prostředích vyšší a shromáždění generace 0 mohou být mnohem větší. Výkon se zlepší, když aplikace přiděluje více paměti před vyvoláním uvolňování paměti.
Problém: Využití procesoru během uvolňování paměti je příliš vysoké
Využití procesoru bude během uvolňování paměti vysoké. Pokud je na uvolňování paměti vynaloženo značné množství času v procesu, je počet sběrů příliš častý nebo sběr trvá příliš dlouho. Zvýšená rychlost přidělování objektů ve spravované haldě způsobuje častější uvolňování paměti. Snížením míry přidělování se sníží frekvence uvolňování paměti.
Sazby přidělení můžete monitorovat pomocí čítače výkonu Allocated Bytes/second
. Další informace naleznete v tématu Čítače výkonu v .NET.
Doba trvání kolekce je primárně faktorem počtu objektů, které přežijí po přidělení. Správce paměti musí projít velkým množstvím paměti, pokud zbývá sesbírat mnoho objektů. Práce na komprimování přeživších je časově náročná. Chcete-li zjistit, kolik objektů bylo zpracováno během garbage collection, nastavte zarážku v ladicím programu na konci garbage collection pro zadanou generaci.
Kontroly výkonu |
---|
Zjistěte, jestli je vysoké využití procesoru způsobené garbage collection. Nastavte bod přerušení na konci uvolňování paměti. |
Pokyny pro řešení potíží
Tato část popisuje pokyny, které byste měli zvážit při zahájení vyšetřování.
Správa paměti pracovní stanice nebo serveru
Zjistěte, jestli používáte správný typ sběru odpadu. Pokud vaše aplikace používá více vláken a instancí objektů, použijte místo správy paměti pracovní stanice správu paměti serveru. Uvolňování paměti serveru funguje na více vláknech, zatímco uvolňování paměti pracovní stanice vyžaduje více instancí aplikace ke spuštění vlastních vláken uvolňování paměti a soutěžit o čas procesoru.
Aplikace, která má nízké zatížení a provádí úlohy zřídka na pozadí, jako je služba, může použít uvolňování paměti pracovní stanice se zakázaným souběžným uvolňováním paměti.
Kdy měřit velikost spravované haldy
Pokud nepoužíváte profiler, budete muset vytvořit konzistentní model měření, abyste mohli efektivně diagnostikovat problémy s výkonem. Při vytváření plánu zvažte následující body:
- Pokud změříte po uvolňování paměti 2. generace, bude celá spravovaná halda bez odpadu (mrtvých objektů).
- Pokud měříte okamžitě po uvolňování paměti generace 0, objekty v generacích 1 a 2 se ještě neshromáždí.
- Pokud měříte bezprostředně před garbage kolekcí, budete měřit co nejvíce přidělení před spuštěním garbage kolekce.
- Měření během sběru odpadu je problematické, protože datové struktury sběrače odpadu nejsou ve stavu platném procházení a nemusí poskytnout úplné výsledky. Toto chování je úmyslné.
- Pokud používáte garbage collection na pracovní stanici se souběžným uvolňováním, uvolněné objekty nejsou kompaktované, takže velikost haldy může být stejná nebo větší (fragmentace může způsobit, že se zdá být větší).
- Když je zatížení fyzické paměti příliš vysoké, dochází ke zpoždění souběžného uvolňování paměti na generaci 2.
Následující postup popisuje, jak nastavit bod přerušení, abyste mohli měřit spravovanou haldu.
Umístit breakpoint na konci garbage collection
Ve WinDbg s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"
Nastavte
GcCondemnedGeneration
na požadovanou generaci. Tento příkaz vyžaduje privátní symboly.Tento příkaz vynutí přerušení, pokud se
RestartEE
spustí po uvolnění objektů generace 2 k uvolnění paměti.V uvolňování paměti serveru volá pouze jedno vlákno
RestartEE
, takže zarážka bude probíhat pouze jednou během uvolňování paměti generace 2.
Postupy kontroly výkonu
Tato část popisuje následující postupy izolace příčiny vašeho problému s výkonem:
- Určete, jestli je problém způsoben garbage collectorem.
- Určete, jestli je výjimka vyčerpání paměti řízena.
- Určete, kolik virtuální paměti je možné rezervovat.
- Určete, jestli je dostatek fyzické paměti.
- Určete, kolik paměti spravovaná halda přiděluje.
- Určete, kolik paměti si spravovaná halda rezervuje.
- Identifikujte velké objekty ve druhé generaci.
- Umožňuje určit odkazy na objekty.
- Určete, jestli byl finalizátor spuštěn.
- Určete, jestli existují objekty, které čekají na dokončení.
- Určete množství volného místa ve spravované haldě.
- Určete počet připnutých objektů.
- Určete délku trvání v rámci procesu uvolňování paměti.
- Určete, co aktivovalo uvolňování paměti.
- Zjistěte, jestli je vysoké využití procesoru způsobené garbage collection.
Určení, jestli je problém způsobený uvolňováním paměti
Projděte si následující dva čítače výkonu paměti:
% čas v GC. Zobrazí procento uplynulého času stráveného provedením uvolňování paměti po posledním cyklu uvolňování paměti. Pomocí tohoto čítače určete, zda systém uvolňování paměti tráví příliš mnoho času na zpřístupnění spravovaného prostoru haldy. Pokud je čas strávený v garbage collection poměrně nízký, může to znamenat problém s prostředkem mimo spravovanou haldu. Tento čítač nemusí být přesný při souběžném nebo na pozadí probíhajícím uvolňování paměti.
# Celkový počet potvrzených bajtů. Zobrazuje množství virtuální paměti aktuálně přidělené správcem paměti. Pomocí tohoto čítače určete, jestli je paměť spotřebovaná správcem paměti neúměrnou částí paměti, kterou vaše aplikace používá.
Většina čítačů výkonu paměti se aktualizuje na konci každé uvolňování paměti. Proto nemusí odrážet aktuální podmínky, o kterých chcete informace.
Určení, jestli je výjimka mimo paměť spravovaná
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte příkaz print exception (
pe
):!pe
Pokud je výjimka spravovaná, OutOfMemoryException zobrazí se jako typ výjimky, jak je znázorněno v následujícím příkladu.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):
Pokud výstup neurčí výjimku, musíte určit, ze kterého vlákna pochází výjimka z nedostatku paměti. Zadáním následujícího příkazu v ladicím programu zobrazíte všechna vlákna se všemi zásobníky volání.
~\*kb
Vlákno s výjimečnými voláními ve svém zásobníku je označeno argumentem
RaiseTheException
. Toto je objekt spravované výjimky.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
K výpisu vnořených výjimek můžete použít následující příkaz.
!pe -nested
Pokud nenajdete žádné výjimky, výjimka mimo paměť pochází z nespravovaného kódu.
Určení, kolik virtuální paměti je možné rezervovat
Ve WinDbg s načteným rozšířením ladicího programu SOS zadejte následující příkaz pro získání největší volné oblasti:
!address -summary
Zobrazí se největší bezplatná oblast, jak je znázorněno v následujícím výstupu.
Largest free region: Base 54000000 - Size 0003A980
V tomto příkladu je velikost největší volné oblasti přibližně 24000 kB (3A980 v šestnáctkové soustavě). Tato oblast je mnohem menší než ta, kterou garbage collector potřebuje pro segment.
-nebo-
Použijte příkaz
vmstat
:!vmstat
Největší bezplatná oblast je největší hodnotou ve sloupci MAXIMUM, jak je znázorněno v následujícím výstupu.
TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~~ ~~~~ Free: Small 8K 64K 46K 36 1,671K Medium 80K 864K 349K 3 1,047K Large 1,384K 1,278,848K 151,834K 12 1,822,015K Summary 8K 1,278,848K 35,779K 51 1,824,735K
Určení, jestli je dostatek fyzické paměti
Spusťte Správce úloh systému Windows.
Performance
Na kartě se podívejte na potvrzenou hodnotu. (Ve Windows 7 se podívejte naCommit (KB)
vSystem group
.)Pokud je
Total
blízkoLimit
, dochází k nedostatku fyzické paměti.
Chcete-li zjistit, kolik paměti spravovaná halda alokuje
# Total committed bytes
Pomocí čítače výkonu paměti získáte počet bajtů, které spravovaná halda potvrdí. Garbage kolektor podle potřeby potvrdí části v segmentu, ne všechny najednou.Poznámka:
Nepoužívejte čítač výkonu
# Bytes in all Heaps
, protože nepředstavuje skutečné využití paměti spravovanou haldou. Velikost generování je zahrnuta v této hodnotě a je ve skutečnosti její prahová velikost, tj. velikost, která indukuje uvolňování paměti, pokud je generování naplněno objekty. Proto je tato hodnota obvykle nula.
Zjistit, kolik paměti si spravovaná halda vyhrazuje
Použijte paměťový čítač výkonu
# Total reserved bytes
.Sběrač paměti si vyhrazuje paměť v segmentech a začátek segmentu můžete určit pomocí příkazu
eeheap
.Důležité
Ačkoli můžete určit množství paměti, které garbage collector přiděluje pro každý segment, velikost segmentu je specifická pro danou implementaci a může se kdykoli změnit, včetně změn při pravidelných aktualizacích. Vaše aplikace by nikdy neměla provádět předpoklady týkající se konkrétní velikosti segmentu ani záviset na ní, ani by se neměla pokoušet konfigurovat množství paměti dostupné pro přidělení segmentů.
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
!eeheap -gc
Výsledek je následující:
Number of GC Heaps: 2 ------------------------------ Heap 0 (002db550) generation 0 starts at 0x02abe29c generation 1 starts at 0x02abdd08 generation 2 starts at 0x02ab0038 ephemeral segment allocation context: none segment begin allocated size 02ab0000 02ab0038 02aceff4 0x0001efbc(126908) Large object heap starts at 0x0aab0038 segment begin allocated size 0aab0000 0aab0038 0aab2278 0x00002240(8768) Heap Size 0x211fc(135676) ------------------------------ Heap 1 (002dc958) generation 0 starts at 0x06ab1bd8 generation 1 starts at 0x06ab1bcc generation 2 starts at 0x06ab0038 ephemeral segment allocation context: none segment begin allocated size 06ab0000 06ab0038 06ab3be4 0x00003bac(15276) Large object heap starts at 0x0cab0038 segment begin allocated size 0cab0000 0cab0038 0cab0048 0x00000010(16) Heap Size 0x3bbc(15292) ------------------------------ GC Heap Size 0x24db8(150968)
Adresy označené segmentem jsou počátečními adresami segmentů.
Určení velkých objektů v generaci 2
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
!dumpheap –stat
Pokud je spravovaná halda velká,
dumpheap
může dokončení chvíli trvat.Můžete začít analyzovat z posledních několika řádků výstupu, protože uvádějí objekty, které používají nejvíce místa. Například:
2c6108d4 173712 14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo 00155f80 533 15216804 Free 7a747c78 791070 15821400 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700930 19626040 System.Collections.Specialized.ListDictionary 2c64e36c 78644 20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo 79124228 121143 29064120 System.Object[] 035f0ee4 81626 35588936 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 40182 90664128 System.Collections.Hashtable+bucket[] 790fa3e0 3154024 137881448 System.String Total 8454945 objects
Poslední uvedený objekt je řetězec a zabírá nejvíce místa. Aplikaci můžete prozkoumat a zjistit, jak je možné optimalizovat objekty řetězců. Pokud chcete zobrazit řetězce v rozsahu 150 až 200 bajtů, zadejte následující:
!dumpheap -type System.String -min 150 -max 200
Příklad výsledků je následující.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …
Použití celého čísla místo řetězce pro ID může být efektivnější. Pokud se stejný řetězec opakuje tisícekrát, zvažte prokládání řetězců. Další informace o prokládání řetězců naleznete v referenčním tématu pro metodu String.Intern .
Určení odkazů na objekty
Ve WinDbg s načteným rozšířením ladicího programu SOS zadejte následující příkaz, který zobrazí odkazy na objekty:
!gcroot
-nebo-
Pokud chcete určit odkazy na konkrétní objekt, uveďte adresu:
!gcroot 1c37b2ac
Kořeny nalezené na hromadách mohou být falešně pozitivní. Další informace potřebujete pomocí příkazu
!help gcroot
.ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)-> 19010b78(DemoApp.FormDemoApp)-> 19011158(System.Windows.Forms.PropertyStore)-> … [omitted] 1c3745ec(System.Data.DataTable)-> 1c3747a8(System.Data.DataColumnCollection)-> 1c3747f8(System.Collections.Hashtable)-> 1c376590(System.Collections.Hashtable+bucket[])-> 1c376c98(System.Data.DataColumn)-> 1c37b270(System.Data.Common.DoubleStorage)-> 1c37b2ac(System.Double[]) Scan Thread 0 OSTHread 99c Scan Thread 6 OSTHread 484
Dokončení
gcroot
příkazu může trvat delší dobu. Jakýkoli objekt, který není uvolněn automatickým uvolňováním paměti, je živý objekt. To znamená, že nějaký kořen přímo nebo nepřímo udržuje objekt, takžegcroot
by se měla vrátit informace o cestě k objektu. Měli byste prozkoumat vrácené grafy a zjistit, proč se na tyto objekty stále odkazují.
Určení, jestli byl finalizátor spuštěn
Spusťte testovací program, který obsahuje následující kód:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Pokud test problém vyřeší, znamená to, že garbage collector nevracel objekty, protože finalizátory pro tyto objekty byly pozastaveny. Metoda GC.WaitForPendingFinalizers umožňuje finalizátorům dokončit své úkoly a opravit problém.
Určení, jestli existují objekty čekající na dokončení
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
!finalizequeue
Podívejte se na počet objektů, které jsou připravené k dokončení. Pokud je číslo vysoké, musíte zkontrolovat, proč tyto finalizátory vůbec nemohou pokračovat nebo nemohou dostatečně rychle pokračovat.
Pokud chcete získat výstup vláken, zadejte následující příkaz:
!threads -special
Tento příkaz poskytuje výstup, například následující.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEE
Vlákno finalizátoru označuje, který konkrétní finalizátor je aktuálně spuštěn, pokud nějaký. Pokud vlákno finalizátoru nespouští žádné finalizátory, čeká na událost, která mu řekne, aby vykonalo svou práci. Ve většině případů uvidíte vlákno finalizátoru v tomto stavu, protože běží na THREAD_HIGHEST_PRIORITY a má dokončit spuštění finalizátorů, pokud existuje, velmi rychle.
Určení množství volného místa ve spravované haldě
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
!dumpheap -type Free -stat
Tento příkaz zobrazí celkovou velikost všech volných objektů ve spravované haldě, jak je znázorněno v následujícím příkladu.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objects
Pokud chcete určit volné místo ve generaci 0, zadejte následující příkaz pro informace o spotřebě paměti podle generace:
!eeheap -gc
Tento příkaz zobrazí výstup podobný následujícímu. Poslední řádek zobrazuje dočasný segment.
Heap 0 (0015ad08) generation 0 starts at 0x49521f8c generation 1 starts at 0x494d7f64 generation 2 starts at 0x007f0038 ephemeral segment allocation context: none segment begin allocated size 00178250 7a80d84c 7a82f1cc 0x00021980(137600) 00161918 78c50e40 78c7056c 0x0001f72c(128812) 007f0000 007f0038 047eed28 0x03ffecf0(67103984) 3a120000 3a120038 3a3e84f8 0x002c84c0(2917568) 46120000 46120038 49e05d04 0x03ce5ccc(63855820)
Vypočítejte prostor, který používá generace 0:
? 49e05d04-0x49521f8c
Výsledek je následující: Generace 0 je přibližně 9 MB.
Evaluate expression: 9321848 = 008e3d78
Následující příkaz vypíše volné místo v rozsahu generace 0:
!dumpheap -type Free -stat 0x49521f8c 49e05d04
Výsledek je následující:
------------------------------ Heap 0 total 409 objects ------------------------------ Heap 1 total 0 objects ------------------------------ Heap 2 total 0 objects ------------------------------ Heap 3 total 0 objects ------------------------------ total 409 objects Statistics: MT Count TotalSize Class Name 0015a498 409 7296540 Free Total 409 objects
Tento výstup ukazuje, že část haldy 0 generace používá 9 MB místa pro objekty a má 7 MB volného místa. Tato analýza ukazuje rozsah, do kterého generace 0 přispívá k fragmentaci. Toto množství využití haldy by mělo být zlevněno z celkové částky jako příčina fragmentace dlouhodobými objekty.
Určení počtu připnutých objektů
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz:
!gchandles
Zobrazené statistiky zahrnují počet připnutých úchytů, jak ukazuje následující příklad.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Určení doby pro garbage collection
Prozkoumejte čítač paměťového výkonu
% Time in GC
.Hodnota se vypočítá pomocí časového intervalu. Vzhledem k tomu, že se čítače aktualizují na konci každé garbage collection, bude mít aktuální vzorek stejnou hodnotu jako předchozí vzorek, pokud v tomto období nedošlo k žádným kolekcím.
Čas shromažďování se získá vynásobením času intervalu vzorku s procentuální hodnotou.
Následující data ukazují čtyři intervaly vzorkování dvou sekund pro 8sekundovou studii. Sloupce
Gen0
,Gen1
aGen2
zobrazují celkový počet uvolňování paměti, které byly dokončeny do konce intervalu pro danou generaci.Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 10 2 10 3 1 1 3 11 3 1 3 4 11 3 1 3
Tyto informace neukazují, kdy došlo k uvolňování paměti, ale můžete určit počet uvolňování paměti, ke kterým došlo v časovém intervalu. Za předpokladu nejhoršího případu se desátá generace 0 uvolňování paměti dokončila na začátku druhého intervalu a jedenáctá generace 0 uvolňování paměti skončila na konci třetího intervalu. Doba mezi koncem desátého a koncem jedenáctého uvolňování paměti je asi 2 sekundy a čítač výkonu ukazuje 3%, takže doba trvání garbage collection generace 0 při jedenáctém uvolňování paměti byla (2 sekundy * 3%), což je 60 ms.
V dalším příkladu je pět intervalů.
Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 3 2 10 3 1 1 3 11 4 1 1 4 11 4 1 1 5 11 4 2 20
Druhá generace 2 uvolňování paměti začala během čtvrtého intervalu a dokončena v pátém intervalu. Za předpokladu nejhoršího případu bylo poslední uvolňování paměti pro kolekci generace 0, která skončila na začátku třetího intervalu, a uvolňování paměti generace 2 skončilo na konci pátého intervalu. Proto doba mezi koncem uvolňování paměti generace 0 a koncem uvolňování paměti generace 2 činí 4 sekundy. Vzhledem k tomu, že
% Time in GC
čítač je 20%, maximální doba, po kterou mohlo trvat uvolnění paměti odpadkového sběru generace 2, je 800 ms (4 sekundy * 20%).Případně můžete určit délku uvolňování paměti pomocí událostí trasování pro Windows (ETW) a analyzovat informace, abyste určili dobu uvolňování paměti.
Například následující data ukazují sekvenci událostí, které proběhly během nesouběrového uvolňování paměti.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEnd
Pozastavení spravovaného vlákna trvalo 26us (
GCSuspendEEEnd
– ).GCSuspendEEBegin_V1
Skutečné uvolňování paměti trvalo 4,8 ms (
GCEnd_V1
–GCStart_V1
).Obnovení spravovaných vláken trvalo 21 μs (
GCRestartEEEnd
–GCRestartEEBegin
).Následující výstup obsahuje příklad pro uvolňování paměti na pozadí a obsahuje pole procesu, vlákna a události. (Nezobrazují se všechna data.)
timestamp(us) event name process thread event field 42504385 GCSuspendEEBegin_V1 Test.exe 4372 1 42504648 GCSuspendEEEnd Test.exe 4372 42504816 GCStart_V1 Test.exe 4372 102019 42504907 GCStart_V1 Test.exe 4372 102020 42514170 GCEnd_V1 Test.exe 4372 42514204 GCHeapStats Test.exe 4372 102020 42832052 GCRestartEEBegin Test.exe 4372 42832136 GCRestartEEEnd Test.exe 4372 63685394 GCSuspendEEBegin_V1 Test.exe 4744 6 63686347 GCSuspendEEEnd Test.exe 4744 63784294 GCRestartEEBegin Test.exe 4744 63784407 GCRestartEEEnd Test.exe 4744 89931423 GCEnd_V1 Test.exe 4372 102019 89931464 GCHeapStats Test.exe 4372
Událost
GCStart_V1
v 42504816 označuje, že se jedná o uvolňování paměti na pozadí, protože poslední pole je1
. Tím se stane sběr odpadu č. 102019.GCStart
událost nastane, protože než spustíte uvolňování paměti na pozadí, je potřeba efemérní uvolňování paměti. Tím se stane sběr odpadu č. 102020.Při 42514170 končí správa paměti č. 102020. Spravovaná vlákna se v tuto chvíli restartují. To je dokončeno ve vlákně 4372, které aktivovalo tento proces uvolňování paměti na pozadí.
Na vlákně 4744 dochází k pozastavení. Jedná se o jedinou dobu, kdy musí uvolňování paměti na pozadí pozastavit činnost spravovaných vláken. Tato doba trvání je přibližně 99 ms ((63784407-63685394)/1000).
Událost
GCEnd
pro uvolňování paměti na pozadí má hodnotu 89931423. To znamená, že uvolňování paměti na pozadí trvalo přibližně 47 sekund ((89931423-42504816)/1000).Zatímco běží spravovaná vlákna, můžete vidět libovolný počet pomíjivých uvolňování paměti, ke kterým dochází.
Určení toho, co aktivovalo uvolňování paměti
V ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte následující příkaz, který zobrazí všechna vlákna s jejich zásobníky volání:
~*kb
Tento příkaz zobrazí výstup podobný následujícímu.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
Pokud uvolňování paměti způsobilo oznámení o nedostatku paměti z operačního systému, zásobník volání je podobný s tím rozdílem, že vlákno je finalizační vlákno. Vlákno finalizátoru obdrží asynchronní oznámení o nedostatku paměti a vyvolá garbage collection.
Pokud byla uvolnění paměti způsobena přidělením paměti, zásobník se zobrazí takto:
0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration 0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1 0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18 0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b 0012f310 7a02ae4c mscorwks!Alloc+0x60 0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd 0012f424 300027f4 mscorwks!JIT_NewArr1+0x148 000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c 0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153
Právě včasný pomocník (
JIT_New*
) nakonec zavoláGCHeap::GarbageCollectGeneration
. Pokud zjistíte, že uvolňování paměti 2. generace je způsobeno alokacemi, musíte určit, které objekty jsou shromažďovány uvolňováním paměti 2. generace a jak se jim vyhnout. To znamená, že chcete určit rozdíl mezi začátkem a ukončením procesu uvolňování paměti pro 2. generaci a objekty, které tento sběr způsobily.Například zadejte do debuggeru následující příkaz, který zobrazí počátek sbírky generace 2:
!dumpheap –stat
Příklad výstupu (zkrácený, aby se zobrazily objekty, které používají nejvíce místa):
79124228 31857 9862328 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 00155f80 21248 12256296 Free 79103b6c 297003 13068132 System.Threading.ReaderWriterLock 7a747ad4 708732 14174640 System.Collections.Specialized.HybridDictionary 7a747c78 786498 15729960 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 035f0ee4 89192 38887712 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 7912c444 91616 71887080 System.Double[] 791242ec 32451 82462728 System.Collections.Hashtable+bucket[] 790fa3e0 2459154 112128436 System.String Total 6471774 objects
Opakujte příkaz na konci generace 2:
!dumpheap –stat
Příklad výstupu (zkrácený, aby se zobrazily objekty, které používají nejvíce místa):
79124228 26648 9314256 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 79103b6c 296770 13057880 System.Threading.ReaderWriterLock 7a747ad4 708730 14174600 System.Collections.Specialized.HybridDictionary 7a747c78 786497 15729940 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 00155f80 13806 34007212 Free 035f0ee4 89187 38885532 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 32370 82359768 System.Collections.Hashtable+bucket[] 790fa3e0 2440020 111341808 System.String Total 6417525 objects
Objekty
double[]
zmizely z konce výstupu, což znamená, že byly shromážděny. Tyto objekty představují přibližně 70 MB. Zbývající objekty se moc nezměnily. Proto tytodouble[]
objekty byly důvodem, proč došlo k této generaci 2 uvolňování paměti. Vaším dalším krokem je určit, pročdouble[]
tam jsou objekty a proč zemřely. Můžete se zeptat vývojáře kódu, odkud tyto objekty pocházejí, nebo můžete použítgcroot
příkaz.
Určení, jestli je vysoké využití procesoru způsobené garbage collectorem
Propojte hodnotu čítače
% Time in GC
výkonu paměti s dobou trvání procesu.Pokud hodnota
% Time in GC
vystoupá současně s časem procesu, způsobuje uvolňování paměti vysoké využití procesoru. V opačném případě profilujte aplikaci a zjistěte, kde dochází k vysokému využití.