Uvolňování paměti a výkon
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í problémů s využitím paměti a uvolňováním paměti. 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 Paměť .NET CLR čítačů výkonu, jak je popsáno v čítačích výkonu v .NET, poskytuje informace o uvolňování paměti.
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 uvolně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 GCStart_V1
událost, která se vyvolá při spuštění 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í pro Windows je efektivní a nezamaskuje žádné problémy s výkonem související s uvolňováním paměti. Proces může poskytovat vlastní události ve spojení s událostmi Trasování událostí pro Windows. Při protokolování je možné korelovat události aplikace i události uvolňování paměti a určit, jak a kdy dojde 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
Rozhraní clr (Common Language Runtime) profilace poskytují podrobné informace o objektech, které byly ovlivněny během uvolňování paměti. Profiler může být upozorněn při spuštění a ukončení uvolňování paměti. Může poskytovat sestavy o objektech ve 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.
Sledování prostředků domény aplikace
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čení, jestli je problém skutečně uvolňování paměti. Pokud zjistíte, že se jedná o řešení problému, vyberte v následujícím seznamu.
- Vyvolá se výjimka mimo paměť
- Proces používá příliš mnoho paměti.
- Uvolňování paměti nevyvolá objekty dostatečně rychle.
- Spravovaná halda je příliš fragmentovaná.
- Pozastavení uvolňování paměti je příliš dlouhé
- Generace 0 je příliš velká
- Využití procesoru během uvolňování paměti je příliš vysoké
Problém: Došlo k výjimce mimo paměť
Existují dva legitimní případy, kdy může dojít k vyvolání spravovaného OutOfMemoryException objektu:
Dochází k virtuální paměti.
Systém uvolňování paměti přiděluje paměť 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 přidělení.
Kontroly výkonu |
---|
Určete, jestli je výjimka mimo paměť spravovaná. 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 spravovaného nedostatku paměti.
- Výpis paměti zaplněný.
- 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: Uvolňování paměti nevyvolá objekty dostatečně rychle
Pokud se zdá, že objekty nejsou uvolněny podle očekávání pro uvolňování paměti, je nutné určit, zda existují silné odkazy na tyto 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í. U generace 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ě sbalené do jednoho prostoru, aby vyhovovaly velkým žádostem o přidělení 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á, bylo možné připnout příliš mnoho objektů.
Pokud fragmentace virtuální paměti brání uvolňování paměti v přidávání segmentů, může to být jedna z následujících příčin:
Časté načítání a uvolňování mnoha malých sestavení.
Při spolupráci s nespravovaným kódem držíte příliš mnoho odkazů na objekty MODELU COM.
Vytvoření velkých přechodných objektů, což způsobuje, že velká halda objektu často přiděluje a uvolní segmenty haldy.
Při hostování modulu CLR může aplikace požádat, aby systém uvolňování paměti 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é
Uvolňování paměti funguje v měkkém reálném čase, takže aplikace musí být schopná tolerovat některé pozastavení. Kritériem pro měkký reálný čas je, že 95 % operací musí být dokončeno včas.
V souběžné uvolňování paměti mohou spravovaná vlákna běžet během shromažďování, což znamená, že pozastavení je 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 2. generace ale můžete snížit změnou vzoru žádostí o přidělení aplikací.
Další přesnější metodou je použití událostí trasování událostí pro Windows uvolňování paměti. Časování kolekcí najdete přidáním rozdílů časových razítek pro posloupnost událostí. Celá sekvence kolekce zahrnuje pozastavení prováděcího modulu, samotné uvolňování paměti a obnovení prováděcí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 dobu v 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 pracovní stanice používáte uvolňování paměti serveru. Důvodem je to, že prahová hodnota pro aktivaci uvolňování paměti 0 generace je v těchto prostředích vyšší a kolekce generace 0 mohou být mnohem větší. Výkon se zlepší, když aplikace přiděluje více paměti před aktivací 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 v uvolňování paměti vynaloženo značné množství času procesu, je počet kolekcí příliš častý nebo kolekce trvá příliš dlouho. Vyšší míra 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í. Uvolňování paměti musí projít velkým množstvím paměti, pokud mnoho objektů zůstane shromážděno. Práce na komprimování přeživších je časově náročná. Chcete-li zjistit, kolik objektů bylo zpracováno během kolekce, nastavte zarážku v ladicím programu na konci uvolňování paměti pro zadanou generaci.
Kontroly výkonu |
---|
Zjistěte, jestli je vysoké využití procesoru způsobené uvolňováním paměti. Nastavte zarážku 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í.
Uvolňování paměti pracovní stanice nebo serveru
Zjistěte, jestli používáte správný typ uvolňování paměti. Pokud vaše aplikace používá více vláken a instancí objektů, použijte místo uvolňování paměti pracovní stanice uvolňování 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 2. generaci uvolňování paměti, bude celá spravovaná halda bez uvolňování paměti (mrtvé objekty).
- 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 uvolňováním paměti, změříte co nejvíce přidělení před spuštěním uvolňování paměti.
- Měření během uvolňování paměti je problematické, protože datové struktury systému uvolňování paměti nejsou v platném stavu pro procházení a nemusí být schopny poskytnout úplné výsledky. Toto chování je úmyslné.
- Pokud používáte uvolňování paměti pracovní stanice se souběžným uvolňováním paměti, uvolněné objekty nejsou komprimované, takže velikost haldy může být stejná nebo větší (fragmentace může být větší).
- Souběžné uvolňování paměti na generaci 2 se zpozdí, když je zatížení fyzické paměti příliš vysoké.
Následující postup popisuje, jak nastavit zarážku, abyste mohli měřit spravovanou haldu.
Nastavení zarážky na konci uvolňování paměti
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
RestartEE
se spustí po uvolnění objektů generace 2 pro uvolňová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 uvolňováním paměti.
- Určete, jestli je výjimka mimo paměť spravovaná.
- 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 potvrdí.
- Určete, kolik paměti si spravovaná halda vyhrazuje.
- Určete velké objekty v generaci 2.
- 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 dobu v uvolňování paměti.
- Určete, co aktivovalo uvolňování paměti.
- Zjistěte, jestli je vysoké využití procesoru způsobené uvolňováním paměti.
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:
% času 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, jestli systém uvolňování paměti tráví příliš mnoho času, aby byl dostupný spravovaný prostor haldy. Pokud je čas strávený v uvolňování paměti relativně nízký, může to znamenat problém s prostředkem mimo spravovanou haldu. Tento čítač nemusí být přesný, pokud je zapojen souběžný uvolňování paměti nebo uvolňování paměti na pozadí.
# Celkový počet potvrzených bajtů. Zobrazí velikost virtuální paměti aktuálně potvrzené systémem uvolňování paměti. Pomocí tohoto čítače určete, jestli je paměť spotřebovaná uvolňováním paměti nadmě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, které vlákno výjimku z nedostatku paměti pochází. Zadáním následujícího příkazu v ladicím programu zobrazíte všechna vlákna s jejich zásobníky volání:
~\*kb
Vlákno se zásobníkem, který má volání výjimek, je označen 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, který získá největší bezplatnou oblast:
!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ž to, co systém uvolňování paměti potřebuje pro segment.
-Nebo-
vmstat
Použijte příkaz:!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)
.System group
)Pokud je blízko
Total
fyzické paměti, dochází k nedostatkuLimit
fyzické paměti.
Určení, kolik paměti spravovaná halda potvrdí
# Total committed bytes
Pomocí čítače výkonu paměti získáte počet bajtů, které spravovaná halda potvrdí. Systém uvolňování paměti podle potřeby potvrdí bloky dat 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.
Určení, kolik paměti si spravovaná halda vyhrazuje
Použijte čítač výkonu
# Total reserved bytes
paměti.Systém uvolňování paměti si vyhrazuje paměť v segmentech a pomocí příkazu můžete určit, kde segment začíná
eeheap
.Důležité
I když můžete určit velikost paměti, kterou systém uvolňování paměti přiděluje pro každý segment, velikost segmentu je specifická pro implementaci a může se kdykoli změnit, včetně pravidelných aktualizací. 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. Pří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 stackech 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. Libovolný objekt, který není uvolněný uvolňováním paměti, je živý objekt. To znamená, že některý kořenový adresář přímo nebo nepřímo drží na objektu, takžegcroot
by se měly 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 uvolňování paměti neobsáhlo objekty, protože finalizační metody 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ý finalizátor je aktuálně spuštěn. Pokud vlákno finalizátoru nespouštět žádné finalizátory, čeká na událost, aby jí řekla, aby udělala 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 vygeneruje volné místo v rozsahu 0 generace:
!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 v uvolňování paměti
Prozkoumejte čítač výkonu
% Time in GC
paměti.Hodnota se vypočítá pomocí ukázkového intervalu. Vzhledem k tomu, že se čítače aktualizují na konci každé uvolňování paměti, bude mít aktuální ukázka stejnou hodnotu jako předchozí vzorek, pokud během intervalu 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
aGen2
,Gen1
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 se nezobrazují, 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 intervalu času. 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í jedenácté generace 0 uvolňování paměti byla (2 sekundy * 3 % = 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 je doba mezi koncem generování 0 uvolňování paměti a koncem uvolňování paměti 2. generace 4 sekundy. Vzhledem k tomu, že
% Time in GC
čítač je 20 %, maximální doba potřebná pro uvolňování paměti generace 2 je (4 sekundy * 20 % = 800 ms).Případně můžete určit délku uvolňování paměti pomocí událostí trasování událostí pro Windows uvolňování paměti a analyzovat informace, abyste určili dobu uvolňování paměti.
Například následující data ukazují sekvenci událostí, ke které došlo 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 21us (
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 uvolňování paměti Ne. 102019.K
GCStart
události dochází, protože před spuštěním uvolňování paměti na pozadí je potřeba dočasné uvolňování paměti. Tím se stane uvolňování paměti č. 102020.V 42514170 skončí uvolňování paměti č. 102020. Spravovaná vlákna se v tuto chvíli restartují. To je dokončeno ve vlákně 4372, které aktivovalo tuto uvolňování paměti na pozadí.
Na vlákně 4744 dochází k pozastavení. Jedná se o jediný čas, kdy musí uvolňování paměti na pozadí pozastavit spravovaná vlákna. Tato doba trvání je přibližně 99 ms ((63784407-63685394)/1000).
Událost
GCEnd
uvolňování paměti na pozadí je na 89931423. To znamená, že uvolňování paměti na pozadí trvalo přibližně 47seconds ((89931423-42504816)/1000).Zatímco jsou spravovaná vlákna spuštěná, můžete vidět libovolný počet dočasný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 získá asynchronní oznámení o nedostatku paměti a vyvolá uvolňování paměti.
Pokud uvolnění paměti způsobilo přidělení 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
Pomocná rutina za běhu (
JIT_New*
) nakonec zavoláGCHeap::GarbageCollectGeneration
. Pokud zjistíte, že uvolňování paměti 2. generace je způsobeno přidělením, musíte určit, které objekty jsou shromažďovány uvolňováním paměti generace 2 a jak se jim vyhnout. To znamená, že chcete určit rozdíl mezi startem a koncem uvolňování paměti generace 2 a objekty, které způsobily kolekci generace 2.Zadejte například do ladicího programu následující příkaz, který zobrazí začátek kolekce 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é uvolňováním paměti
Korelujte hodnotu čítače
% Time in GC
výkonu paměti s časem procesu.% Time in GC
Pokud se hodnota špičky současně s časem procesu, uvolňování paměti způsobuje vysoké využití procesoru. V opačném případě profilujte aplikaci a zjistěte, kde dochází k vysokému využití.