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

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

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

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

  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, 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
    
  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, 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

  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) . System group)

    Pokud je blízko Total fyzické paměti, dochází k nedostatku Limitfyzické 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že gcroot 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í

  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ý 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 Gen0a Gen2 , Gen1zobrazují 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_V1GCStart_V1).

    Obnovení spravovaných vláken trvalo 21us (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 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 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é 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í.

Viz také