Sdílet prostřednictvím


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í 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í.

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í.

Kontroly výkonu
Určete, kolik virtuální paměti je možné rezervovat.
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.

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č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á

  1. 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):
    
  2. 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
    
  3. 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

  1. Spusťte Správce úloh systému Windows.

  2. Performance Na kartě se podívejte na potvrzenou hodnotu. (Ve Windows 7 se podívejte na Commit (KB) v System group.)

    Pokud je Total blízko Limit, 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že gcroot 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í

  1. 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.

  2. 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 a Gen2 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_V1GCStart_V1).

    Obnovení spravovaných vláken trvalo 21 μs (GCRestartEEEndGCRestartEEBegin).

    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 je 1. 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 tyto double[] 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žít gcroot 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í.

Viz také