Uvolnění paměti a výkon

Toto téma popisuje problémy související s uvolňováním paměti a využitím paměti. Řeší problémy související se spravovanou haldou a vysvětluje, jak minimalizovat účinek uvolňování paměti na 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 uvolňováním paměti. Postupy uvedené dále v tomto tématu se vztahují k těmto nástrojům.

Čítače výkonu paměti

Čítače výkonu můžete použít ke shromažďování dat o výkonu. Pokyny najdete v tématu Profilace modulu runtime. Kategorie paměti .NET CLR čítačů výkonu, jak je popsáno v tématu Čítače výkonu v rozhraní .NET, poskytuje informace o systému uvolňování paměti.

Ladění pomocí SOS

Ke kontrole objektů na spravované haldě můžete použít Windows Debugger (WinDbg).

Pokud chcete nainstalovat WinDbg, nainstalujte ladicí nástroje pro Windows na stránce Stáhnout ladicí nástroje pro Windows nástroje.

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í, kterou poskytuje .NET. Počínaje .NET Framework 4 zachycují události Trasování událostí pro Windows pro uvolňování paměti užitečné informace pro analýzu spravované haldy ze statistického hlediska. Například událost, která je vyvolána, když dojde k uvolňování GCStart_V1 paměti, poskytuje následující informace:

  • Která generace objektů se shromažďuje.

  • Co aktivoval uvolňování paměti.

  • Typ uvolňování paměti (souběžné nebo ne souběžné)

Protokolování událostí trasová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 zaprotokolování je možné korelovat události aplikace i události uvolňování paměti, aby bylo možné 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 konci požadavku klienta.

Rozhraní API pro profilaci

Rozhraní profilace modulu CLR (Common Language Runtime) 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átory mohou poskytovat komplexní informace. Složité profilátory ale mohou potenciálně upravovat chování aplikace.

Sledování prostředků domény aplikace

Počínaje .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 ve skutečnosti uvolňování paměti. Pokud zjistíte, že je, vyberte z následujícího seznamu a problém vyřešte.

Problém: Vyvolá se výjimka, která je mimo paměť.

Existují dva legitimní případy, kdy je možné spravovaný OutOfMemoryException objekt vyvolat:

  • Došla vám virtuální paměť.

    Systém uvolňování paměti přiděluje paměť systému v segmentech předem určené velikosti. Pokud přidělení vyžaduje další segment, ale ve virtuálním paměťovém prostoru procesu nezůzí žádný souvislé volné bloky, přidělení pro spravovanou haldu selže.

  • Nemá dostatek fyzické paměti pro přidělení.

Kontroly výkonu
Zjistěte, jestli je výjimka mimo paměť spravovaná.

Určete, kolik virtuální paměti je možné rezervovat.

Zjistěte, jestli je k dispozici dostatek fyzické paměti.

Pokud zjistíte, že výjimka není legitimní, obraťte se na oddělení služeb zákazníkům a podpoře Microsoftu s následujícími informacemi:

  • Zásobník s výjimkou spravovaného stavu mimo paměť.

  • Úplný výpis paměti.

  • Data, která prokazují, že se jedná 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 Windows Správce úloh může značit, 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, musíte změřit spravovanou haldu v průběhu času, abyste zjistili případné vzory.

Pokud zjistíte, že problém není způsobený spravovanou haldou, 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 potvrzuje.

Určete, kolik paměti si spravovaná halda rezervuje.

Určení velkých objektů ve 2. generaci

Určí odkazy na objekty.

Problém: Systém uvolňování paměti neuvolňuje objekty dostatečně rychle.

Pokud se zdá, že objekty nejsou uvolněny podle očekávání pro uvolňování paměti, musíte určit, zda existují nějaké silné odkazy na tyto objekty.

K tomuto problému může dojít také v případě, že pro generaci, která obsahuje neuměný objekt, nebyl uvolněn žádný systém uvolňování paměti, což značí, že finalizační metoda pro nespouštěný objekt nebyla spuštěna. To je možné například v případě, že používáte aplikaci sta (single-threaded) a vlákno, které frontu finalizační metody poskytuje, do ní nemůže volat.

Kontroly výkonu
Zkontrolujte odkazy na objekty.

Zjistěte, jestli byla spuštěna finalizační metoda.

Určete, zda existují objekty čekající 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 generaci. U 2. generace 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.

Velké množství volného místa v generaci 0 není problém, protože se jedná o generaci, ve které se přidělují nové objekty.

Fragmentace se vždy vyskytuje ve velké haldě objektů, protože není komprimovaná. Volné objekty, které sousedí, jsou přirozeně sbalené do jednoho prostoru, aby splňovaly požadavky na přidělení velkých objektů.

Fragmentace se může stát problémem ve 1. a 2. generaci. Pokud mají tyto generace po uvolnění paměti velké množství volného místa, může použití objektů aplikace potřebovat úpravy 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 se připnout příliš mnoho objektů.

Pokud fragmentace virtuální paměti brání systému uvolňování paměti v přidávání segmentů, může to mít jednu z následujících příčin:

  • Časté načítání a uvolňování mnoha malých sestavení.

  • Při vzájemné spolupráci s nespravovaným kódem podržíte příliš mnoho odkazů na objekty COM.

  • Vytváření velkých přechodných objektů, což způsobuje, že velká halda objektů často přiděluje a volné 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 snižuje frekvence přidělování segmentů. Toho lze dosáhnout pomocí příznaku STARTUP_HOARD_GC_VM ve výčtu STARTUP_FLAGS .

Kontroly výkonu
Určete množství volného místa ve spravované haldě.

Určete počet připnutých objektů.

Pokud se domníváte, že neexistují žádné oprávněné příčiny pro fragmentaci, obraťte se na zákaznickou službu a podporu Microsoftu.

Problém: pozastavení uvolňování paměti jsou moc dlouhá.

Uvolňování paměti funguje v tiché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 to, že 95% operací se musí dokončit včas.

V případě souběžného uvolňování paměti můžou spravovaná vlákna běžet během shromažďování, což znamená, že pozastavení jsou velmi minimální.

Dočasné uvolňování paměti (generace 0 a 1) jsou poslední jenom několik milisekund, takže snížení pauz většinou není proveditelné. Můžete ale snížit počet pozastavení v kolekcích 2. generace změnou vzoru požadavků na přidělení aplikací.

Další přesnější metodou je použít události ETW pro uvolňování paměti. Časování pro kolekce můžete najít přidáním rozdílů časových razítek pro posloupnost událostí. Celá sekvence kolekce zahrnuje pozastavení prováděcího modulu, uvolňování paměti samotného a obnovení spouštěcího modulu.

Pomocí oznámení o uvolňování paměti můžete zjistit, jestli se server chystá mít kolekci 2. generace, a jestli žádosti o přesměrování na jiný server můžou způsobit 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 moc velká.

Generace 0 pravděpodobně bude mít větší počet objektů v 64 systému, zvlášť když použijete uvolňování paměti serveru místo uvolnění paměti pracovní stanice. Důvodem je to, že prahová hodnota pro aktivaci uvolňování paměti generace 0 je v těchto prostředích vyšší, a kolekce generace 0 může být mnohem větší. Zvýšení výkonu je vylepšeno, pokud aplikace přiděluje více paměti před aktivací uvolňování paměti.

Problém: využití CPU během uvolňování paměti je příliš vysoké.

Využití CPU bude během uvolňování paměti vysoké. Pokud se v uvolňování paměti stráví významné množství času zpracování, počet kolekcí je příliš častý nebo kolekce je trvalá příliš dlouho. Zvýšená míra přidělení objektů na spravované haldě způsobuje, že se uvolňování paměti objevuje častěji. Snížení míry přidělení omezí četnost uvolňování paměti.

Sazby přidělení můžete sledovat pomocí Allocated Bytes/second čítače výkonu. Další informace najdete v tématu čítače výkonu v rozhraní .NET.

Doba trvání kolekce je primárně faktorem počtu objektů, které jsou po přidělení zachovány. Systém uvolňování paměti musí projít velkým množstvím paměti, pokud je stále shromažďováno mnoho objektů. Práce na komprimaci pozůstalých je časově náročná. Chcete-li určit, 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ůsobeno 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 vzít v úvahu při zahájení šetření.

Uvolnění paměti pracovní stanice nebo serveru

Určete, zda 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ích stanic Server uvolňování paměti serveru. Uvolňování paměti serveru funguje ve více vláknech, zatímco uvolňování paměti pracovní stanice vyžaduje více instancí aplikace, aby spouštěla vlastní vlákna uvolňování paměti a mohla konkurovat času procesoru.

Aplikace, která má nízké zatížení a provádí úlohy na pozadí, jako je například 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 změřit velikost spravované haldy

Pokud nepoužíváte Profiler, budete muset vytvořit jednotný měřicí vzor pro efektivní diagnostiku problémů s výkonem. Pro vytvoření plánu Vezměte v úvahu následující body:

  • Pokud měříte po uvolnění paměti 2. generace, celá spravovaná halda bude bez uvolnění paměti (nedoručené objekty).

  • Pokud měříte hned po uvolnění paměti generace 0, objekty v generacích 1 a 2 nebudou dosud shromažďovány.

  • Pokud měříte těsně před uvolňováním paměti, měříte co nejvíce přidělení, než začne uvolňování paměti.

  • Měření během uvolňování paměti je problematické, protože datové struktury uvolňování paměti nejsou v platném stavu pro procházení a nemusí být schopné poskytnout kompletní výsledky. Toto chování je úmyslné.

  • Pokud používáte uvolňování paměti pracovní stanice s souběžným uvolňováním paměti, uvolněné objekty se nekomprimuje, takže velikost haldy může být stejná nebo větší (fragmentace se může zdát, že je větší).

  • Souběžné uvolňování paměti v generaci 2 je zpožděno, pokud je zatížení fyzické paměti příliš vysoké.

Následující postup popisuje, jak nastavit zarážku, abyste mohli změřit spravovanou haldu.

Nastavení zarážky na konci uvolňování paměti

  • V programu WinDbg s načteným rozšířením ladicího programu SOS zadejte následující příkaz:

    knihovny mscorwks BP WKS:: GCHeap:: RestartEE "j (DWO (knihovny Mscorwks! WKS:: GCHeap:: GcCondemnedGeneration) = = 2) ' KB '; ' g ' "

    kde GcCondemnedGeneration je nastaveno na požadovanou generaci. Tento příkaz vyžaduje soukromé symboly.

    Tento příkaz vynutí přerušení, pokud je RestartEE provedeno po uvolnění objektů generace 2 pro uvolňování paměti.

    V uvolňování paměti serveru pouze jedno vlákno volá RestartEE, takže během uvolňování paměti 2. generace dojde k zarážce pouze jednou.

Postupy kontroly výkonu

Tato část popisuje následující postupy k izolaci příčiny problému s výkonem:

Zjištění, zda je problém způsoben uvolňováním paměti

  • Prověřte následující dvě čítače výkonu paměti:

    • % Času v GC. Zobrazuje procento uplynulého času stráveného prováděním uvolňování paměti po posledním cyklu uvolňování paměti. Pomocí tohoto čítače určíte, zda je uvolňování paměti příliš mnoho času na zpřístupnění spravovaného prostoru haldy. Pokud je doba strávená uvolňováním paměti poměrně nízká, může to znamenat problém s prostředky mimo spravovanou haldu. Tento čítač nemusí být přesný, pokud je zapojeno souběžné a uvolňování paměti na pozadí.

    • # Celkový počet potvrzených bajtů Zobrazuje velikost virtuální paměti, která je aktuálně potvrzena systémem uvolňování paměti. Pomocí tohoto čítače určíte, zda paměť využívaná systémem uvolňování paměti je nadměrná část paměti, kterou vaše aplikace používá.

    Většina čítačů výkonu paměti se aktualizuje na konci každého uvolňování paměti. Proto nemusí odrážet aktuální podmínky, o kterých chcete získat informace.

Chcete-li určit, zda je výjimka nedostatek paměti spravovaná

  1. v ladicím programu WinDbg nebo Visual Studio s načteným rozšířením ladicího programu SOS zadejte příkaz výjimka tisku (pe):

    ! PE

    Pokud je výjimka spravovaná, OutOfMemoryException je zobrazena 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čuje výjimku, je nutné určit, ze kterého vlákna je výjimka nedostatek paměti. Zadejte následující příkaz v ladicím programu pro zobrazení všech vláken s jejich zásobníky volání:

    ~*Knowledge

    Vlákno se zásobníkem, který obsahuje volání výjimek, je určeno RaiseTheException argumentem. Toto je spravovaný objekt výjimky.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
    
  3. Pomocí následujícího příkazu můžete vypsat vnořené výjimky.

    ! PE – vnořené

    Pokud nenajdete žádné výjimky, výjimka nedostatek paměti pochází z nespravovaného kódu.

Určení, kolik virtuální paměti je možné rezervovat

  • Do 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ě 2 4000 kB (3A980 v šestnáctkovém formátu). Tato oblast je mnohem menší, než systém uvolňování paměti potřebuje pro segment.

    -nebo-

  • Použijte příkaz vmstat:

    !vmstat

    Největší volná oblast je největší hodnota 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
    

Zjištění, jestli je k dispozici dostatek fyzické paměti

  1. Spusťte Windows Správce úloh.

  2. Na kartě Výkon se podívejte na potvrzenou hodnotu. (V Windows 7 se podívejte na Potvrzení (KB) ve skupině Systém.)

    Pokud se celkový součet blíží limitu, dochází vám fyzická paměť.

Určení, kolik paměti spravovaná halda potvrzuje

  • Pomocí čítače výkonu paměti získejte počet bajtů, # Total committed bytes které spravovaná halda potvrzuje. 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, protože nepředstavuje skutečné využití paměti # Bytes in all Heaps spravovanou haldou. Velikost generace je zahrnutá v této hodnotě a je ve skutečnosti její prahovou velikostí, to znamená velikost, která vyvolává uvolňování paměti, pokud je generování naplněno objekty. Proto je tato hodnota obvykle nula.

Určení paměti rezerv spravované haldy

  • Použijte # Total reserved bytes čítač výkonu paměti.

    Systém uvolňování paměti rezervuje paměť v segmentech a pomocí příkazu eeheap můžete určit, kde segment začíná.

    Důležité

    I když můžete určit velikost paměti, kterou systém uvolňování paměti přiděluje pro jednotlivé segmenty, velikost segmentu je specifická pro implementaci a může se kdykoli změnit, a to i v pravidelných aktualizacích. Vaše aplikace by nikdy neměla provádět předpoklady týkající se konkrétní velikosti segmentu nebo na ní záviset, ani by se neměla pokoušet o konfiguraci dostupné paměti pro přidělení segmentů.

  • Do ladicího 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

  • Do ladicího 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á, může dokončení výpisu paměti chvíli trvat.

    Analýzu můžete začít analyzovat z posledních několika řádků výstupu, protože vykreslují 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ím uvedeným objektem je řetězec, který zabírá nejvíce místa. Můžete svou aplikaci prozkoumat a podívat se, jak lze objekty řetězců optimalizovat. Pokud chcete zobrazit řetězce v rozmezí 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 stáže řetězců. Další informace o stáži řetězců najdete v referenčním tématu pro String.Intern metodu .

Určení odkazů na objekty

  • Do windbg s načteným rozšířením ladicího programu SOS zadejte následující příkaz, který vyjádře odkazy na objekty:

    !gcroot

    -or-

  • Pokud chcete určit odkazy na konkrétní objekt, zadejte adresu:

    !gcroot 1c37b2ac

    Kořeny nalezené v zásobníkech mohou být falešně pozitivní. Další informace získáte 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í příkazu gcroot může trvat dlouhou dobu. Každý objekt, který není uvolněn systémem uvolňování paměti, je živý objekt. To znamená, že některý kořen se přímo nebo nepřímo drží na objektu, takže gcroot by měl vrátit informace o cestě k objektu. Měli byste prozkoumat vrácené grafy a podívat se, proč se na tyto objekty stále odkazuje.

Určení, jestli byla spuštěna finalizační metoda

  • 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 systém uvolňování paměti neuvolňuje objekty, protože finalizační metody pro tyto objekty byly pozastaveny. Metoda GC.WaitForPendingFinalizers umožňuje finalizačním metodám provádět úkoly a opravuje problém.

Určení, zda existují objekty čekající na dokončení

  1. Do ladicího 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 prozkoumat, proč tyto finalizační metody nemohou vůbec postupovat nebo nemohou dostatečně rychle postupovat.

  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 finalizační metody označuje, která finalizační metoda (pokud existuje) se právě spouštěná. Pokud vlákno finalizační metody nese používá žádné finalizační metody, čeká na to, až událost řekne, že má provést svou práci. Ve většině času uvidíte vlákno finalizační metody v tomto stavu, protože běží na THREAD_HIGHEST_PRIORITY a spuštění finalizačních metod by mělo být dokončeno, pokud nějaké jsou, velmi rychle.

Určení volného místa ve spravované haldě

  • Do ladicího 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ů na 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 zjistit 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 používaný generováním 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 vy výpisem volného místa 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 generace 0 používá 9 MB místa pro objekty a má 7 MB volného místa. Tato analýza ukazuje, do jaké míry generace 0 přispívá k fragmentaci. Toto množství využití haldy by se mělo snížit z celkového množství jako příčiny fragmentace dlouhodobými objekty.

Určení počtu připnutých objektů

  • Do ladicího 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 popisovačů, 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

  • Zkontrolujte % Time in GC čítač výkonu paměti.

    Hodnota se vypočítá pomocí intervalu vzorku. Vzhledem k tomu, že se čítače aktualizují na konci každého uvolňování paměti, aktuální vzorek bude mít 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 procentuální hodnotou.

    Následující data ukazují čtyři intervaly vzorkování po dvou sekundách pro 8sekundovou studii. Sloupce , a zobrazují počet uvolňování paměti, ke kterým došlo Gen0 Gen1 během tohoto Gen2 intervalu pro toto generování.

    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 nezminí o tom, kdy došlo k uvolnění paměti, ale můžete určit počet uvolňování paměti, ke kterým došlo v časovém intervalu. V případě nejhoršího případu bylo dokončeno uvolňování paměti desáté generace 0 na začátku druhého intervalu a jedenácté uvolňování paměti 0 na konci pátého intervalu. Doba mezi koncem desátého a koncem jedenáctého uvolňování paměti je přibližně 2 sekundy a čítač výkonu zobrazuje 3%, takže doba trvání jedenáctého uvolňování paměti 0 (2 sekundy * 3% = 60ms).

    V tomto příkladu je 5 teček.

    Interval    Gen0    Gen1    Gen2     % Time in GC
            1       9       3       1                3
            2      10       3       1                1
            3      11       4       2                1
            4      11       4       2                1
            5      11       4       2               20
    

    Druhá kolekce paměti 2. generace začala během třetího intervalu a skončila v pátém intervalu. Za nejhorší případ byl poslední uvolňování paměti pro kolekci generace 0, která skončila na začátku druhého intervalu, a uvolnění paměti 2. generace bylo dokončeno na konci pátého intervalu. Proto je čas mezi koncem uvolňování paměti generace 0 a koncem uvolňování paměti 2. generace 4 sekundy. Vzhledem k tomu % Time in GC , že čítač je 20%, pak maximální doba, po kterou by bylo možné učinit uvolňování paměti 2. generace, je (4 sekundy * 20% = 800ms).

  • Alternativně můžete určit délku uvolňování paměti pomocí událostí ETW pro uvolňování pamětia analyzovat informace a určit dobu trvání uvolňování paměti.

    Například následující data ukazují sekvenci události, ke které došlo během nesouběžné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 ( GCSuspendEEEndGCSuspendEEBegin_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 poskytuje příklad pro uvolňování paměti na pozadí a obsahuje pole proces, vlákno a událost. (Ne všechna data jsou zobrazena.)

    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
    

    GCStart_V1Událost v 42504816 označuje, že se jedná o uvolňování paměti na pozadí, protože poslední pole je 1 . To se v tomto případě stalo uvolňováním paměti. 102019.

    K GCStart události dochází, protože před spuštěním uvolňování paměti na pozadí je zapotřebí dočasné uvolňování paměti. To se v tomto případě stalo uvolňováním paměti. 102020.

    V 42514170, č. uvolnění paměti. 102020 dokončí. Spravovaná vlákna jsou v tuto chvíli restartována. Tato operace je dokončena ve vláknu 4372, které aktivovalo tuto kolekci paměti na pozadí.

    Na vlákně 4744 dojde k pozastavení. Toto je jediná doba, s jakou má uvolňování paměti na pozadí pozastavit spravovaná vlákna. Toto trvání je přibližně 99ms ((63784407-63685394)/1000).

    GCEndUdálost pro uvolňování paměti na pozadí je 89931423. To znamená, že uvolňování paměti na pozadí uplynulo pro přibližně 47seconds ((89931423-42504816)/1000).

    I když jsou spuštěná spravovaná vlákna, vidíte libovolný počet dočasných uvolňování paměti, ke kterým dochází.

Určení toho, co vyvolalo 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 pro zobrazení všech vláken s jejich zásobníky volání:

    ~*Knowledge

    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 bylo způsobeno oznámením o nedostatku paměti z operačního systému, je zásobník volání podobný, s tím rozdílem, že vlákno je finalizační vlákno. Finalizační vlákno získá oznámení o asynchronní nedostatku paměti a vyřadí uvolňování paměti.

    Pokud uvolňování paměti bylo způsobeno 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
    

    Nakonec se volá pomocník za běhu ( JIT_New* ) GCHeap::GarbageCollectGeneration . Pokud zjistíte, že jsou uvolňovány paměti generace 2 způsobeny přidělením, je nutné 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 koncem uvolňování paměti 2. generace a objekty, které způsobily kolekci 2. generace.

    Zadejte například následující příkaz v ladicím programu, který zobrazí začátek kolekce generace 2:

    ! dumpheap – stat

    Příklad výstupu (zkráceně pro zobrazení objektů, 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 tento příkaz na konci generace 2:

    ! dumpheap – stat

    Příklad výstupu (zkráceně pro zobrazení objektů, 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
    

    double[]Objekty zmizely na konci výstupu, což znamená, že byly shromážděny. Tyto objekty jsou přibližně 70 MB. Zbývající objekty se nezměnily mnohem. Proto by tyto double[] objekty byly důvodem, proč došlo k této chybě uvolňování paměti 2. generace. V dalším kroku zjistíte, proč double[] jsou objekty tam a proč uhynulé. Můžete požádat vývojáře kódu, ze kterého pocházejí tyto objekty, nebo můžete použít příkaz gcroot .

Určení, zda vysoké využití procesoru je způsobeno uvolňováním paměti

  • Proveďte korelaci % Time in GC hodnoty čítače výkonu paměti s časem procesu.

    Pokud je % Time in GC hodnota špičky ve stejnou dobu jako doba zpracování, uvolňování paměti způsobuje vysoké využití procesoru. V opačném případě profilujte aplikaci, abyste zjistili, kde dochází k vysokému využití.

Viz také