Dela via


Skräpinsamling och prestanda

I den här artikeln beskrivs problem som rör skräpinsamling och minnesanvändning. Den löser problem som gäller den hanterade heapen och förklarar hur du minimerar effekten av skräpinsamling på dina program. Varje problem har länkar till procedurer som du kan använda för att undersöka problem.

Verktyg för prestandaanalys

I följande avsnitt beskrivs de verktyg som är tillgängliga för att undersöka problem med minnesanvändning och skräpinsamling. De procedurer som anges senare i den här artikeln refererar till dessa verktyg.

Minnesprestandaräknare

Du kan använda prestandaräknare för att samla in prestandadata. Anvisningar finns i Körningsprofilering. Kategorin .NET CLR-minne för prestandaräknare, enligt beskrivningen i Prestandaräknare i .NET, innehåller information om skräpinsamlaren.

Felsökning med SOS

Du kan använda Windows-felsökaren (WinDbg) för att inspektera objekt på den hanterade heapen.

Installera WinDbg genom att installera Felsökningsverktyg för Windows från sidan Hämta felsökningsverktyg för Windows .

Skräpinsamling ETW-händelser

Händelsespårning för Windows (ETW) är ett spårningssystem som kompletterar det stöd för profilering och felsökning som tillhandahålls av .NET. Från och med .NET Framework 4 samlar skräpinsamlings-ETW-händelser in användbar information för att analysera den hanterade heapen ur statistisk synvinkel. Händelsen, som utlöses när en skräpinsamling är på väg att inträffa, innehåller till exempel GCStart_V1 följande information:

  • Vilken generering av objekt som samlas in.
  • Vad utlöste skräpinsamlingen?
  • Typ av skräpinsamling (samtidig eller inte samtidig).

ETW-händelseloggning är effektiv och kommer inte att maskera några prestandaproblem som är associerade med skräpinsamling. En process kan tillhandahålla sina egna händelser tillsammans med ETW-händelser. När du loggas kan både programmets händelser och skräpinsamlingshändelser korreleras för att avgöra hur och när heap-problem uppstår. Ett serverprogram kan till exempel tillhandahålla händelser i början och slutet av en klientbegäran.

Profilerings-API:et

CLR-profileringsgränssnitten (Common Language Runtime) ger detaljerad information om de objekt som påverkades under skräpinsamlingen. En profilerare kan meddelas när en skräpinsamling startar och slutar. Den kan tillhandahålla rapporter om objekten på den hanterade heapen, inklusive en identifiering av objekt i varje generation. Mer information finns i Översikt över profilering.

Profilerare kan tillhandahålla omfattande information. Komplexa profilerare kan dock eventuellt ändra ett programs beteende.

Övervakning av programdomänresurser

Från och med .NET Framework 4 gör ARM (Application Domain Resource Monitoring) det möjligt för värdar att övervaka processor- och minnesanvändning per programdomän. Mer information finns i Övervakning av programdomänresurser.

Felsöka prestandaproblem

Det första steget är att avgöra om problemet verkligen är skräpinsamling. Om du bedömer att det är det väljer du i följande lista för att felsöka problemet.

Problem: Ett undantag från slutminnet utlöses

Det finns två legitima fall där en hanterad OutOfMemoryException kan kastas:

  • Det virtuella minnet börjar ta slut.

    Skräpinsamlaren allokerar minne från systemet i segment med en fördefinierad storlek. Om en allokering kräver ytterligare ett segment, men det inte finns något sammanhängande ledigt block kvar i processens virtuella minnesutrymme, misslyckas allokeringen för den hanterade heapen.

  • Det finns inte tillräckligt med fysiskt minne för allokering.

Prestandakontroller
Avgör om undantaget för out-of-memory hanteras.
Avgör hur mycket virtuellt minne som kan reserveras.
Avgör om det finns tillräckligt med fysiskt minne.

Om du anser att undantaget inte är legitimt kontaktar du Microsofts kundtjänst och support med följande information:

  • Stacken med det hanterade undantaget out-of-memory.
  • Fullständig minnesdumpning.
  • Data som bevisar att det inte är ett legitimt undantag utan minne, inklusive data som visar att virtuellt eller fysiskt minne inte är ett problem.

Problem: Processen använder för mycket minne

Ett vanligt antagande är att minnesanvändningen som visas på fliken Prestanda i Windows Task Manager kan indikera när för mycket minne används. Den visningen gäller dock arbetsuppsättningen. Den innehåller inte information om användning av virtuellt minne.

Om du fastställer att problemet orsakas av den hanterade heapen måste du mäta den hanterade heapen över tid för att fastställa eventuella mönster.

Om du fastställer att problemet inte orsakas av den hanterade heapen måste du använda intern felsökning.

Prestandakontroller
Avgör hur mycket virtuellt minne som kan reserveras.
Ta reda på hur mycket minne den hanterade heapen förbinder sig till.
Avgör hur mycket minne den hanterade heapen reserverar.
Fastställ stora objekt i generation 2.
Fastställa referenser till objekt.

Problem: Skräpinsamlaren återtar inte objekt tillräckligt snabbt

När det verkar som om objekt inte frigörs som förväntat för skräpinsamling måste du avgöra om det finns några starka referenser till dessa objekt.

Du kan också stöta på det här problemet om det inte har funnits någon skräpinsamling för den generation som innehåller ett dött objekt, vilket indikerar att slutföraren för det döda objektet inte har körts. Detta är till exempel möjligt när du kör ett sta-program (single-threaded apartment) och tråden som servar finalizer-kön inte kan anropa till den.

Prestandakontroller
Kontrollera referenser till objekt.
Avgör om en finalizer har körts.
Avgör om det finns objekt som väntar på att slutföras.

Problem: Den hanterade heapen är för fragmenterad

Fragmenteringsnivån beräknas som förhållandet mellan ledigt utrymme och det totala allokerade minnet för genereringen. För generation 2 är en acceptabel fragmenteringsnivå högst 20 %. Eftersom generation 2 kan bli mycket stor är förhållandet mellan fragmentering viktigare än det absoluta värdet.

Att ha mycket ledigt utrymme i generation 0 är inte ett problem eftersom det är den generation där nya objekt allokeras.

Fragmentering sker alltid i den stora objekthögen eftersom den inte är komprimerad. Kostnadsfria objekt som ligger intill komprimeras naturligt till ett enda utrymme för att uppfylla stora objektallokeringsbegäranden.

Fragmentering kan bli ett problem i generation 1 och generation 2. Om dessa generationer har en stor mängd ledigt utrymme efter en skräpinsamling kan ett programs objektanvändning behöva ändras och du bör överväga att omvärdera livslängden för långsiktiga objekt.

Överdriven fästning av objekt kan öka fragmenteringen. Om fragmenteringen är hög kan för många objekt ha fästs.

Om fragmentering av virtuellt minne hindrar skräpinsamlaren från att lägga till segment kan orsaken vara något av följande:

  • Frekvent inläsning och avlastning av många små sammansättningar.

  • Innehåller för många referenser till COM-objekt när du samverkar med ohanterad kod.

  • Skapande av stora tillfälliga objekt, vilket gör att den stora objekthögen allokerar och frigör heapsegment ofta.

    När du är värd för CLR kan ett program begära att skräpinsamlaren behåller sina segment. Detta minskar frekvensen för segmentallokeringar. Detta görs med hjälp av flaggan STARTUP_HOARD_GC_VM i STARTUP_FLAGS Uppräkning.

Prestandakontroller
Fastställa mängden ledigt utrymme i den hanterade heapen.
Fastställa antalet fästa objekt.

Om du tror att det inte finns någon legitim orsak till fragmenteringen kontaktar du Microsofts kundtjänst och support.

Problem: Pauser i skräpinsamlingen är för långa

Skräpinsamling fungerar i mjuk realtid, så ett program måste kunna tolerera vissa pauser. Ett kriterium för mjuk realtid är att 95 % av åtgärderna måste slutföras i tid.

I samtidig skräpinsamling tillåts hanterade trådar att köras under en samling, vilket innebär att pauser är mycket minimala.

Tillfälliga skräpsamlingar (generation 0 och 1) varar bara några millisekunder, så det är vanligtvis inte möjligt att minska pauserna. Du kan dock minska pauserna i generation 2-samlingar genom att ändra mönstret för allokeringsbegäranden från ett program.

En annan, mer exakt metod är att använda skräpinsamlings-ETW-händelser. Du hittar tidsinställningar för samlingar genom att lägga till tidsstämpelskillnaderna för en sekvens med händelser. Hela samlingssekvensen innehåller avstängning av körningsmotorn, själva skräpinsamlingen och återupptagandet av körningsmotorn.

Du kan använda skräpinsamlingsaviseringar för att avgöra om en server håller på att ha en samling i generation 2 och om omdistribution av begäranden till en annan server kan underlätta eventuella problem med pauser.

Prestandakontroller
Fastställa hur lång tid det tar i en skräpinsamling.
Ta reda på vad som orsakade en skräpinsamling.

Problem: Generation 0 är för stor

Generation 0 kommer sannolikt att ha ett större antal objekt i ett 64-bitarssystem, särskilt när du använder serverskräpinsamling i stället för arbetsstationsskräpinsamling. Det beror på att tröskelvärdet för att utlösa en skräpinsamling av generation 0 är högre i dessa miljöer, och generation 0-samlingar kan bli mycket större. Prestanda förbättras när ett program allokerar mer minne innan en skräpinsamling utlöses.

Problem: CPU-användningen under en skräpinsamling är för hög

CPU-användningen kommer att vara hög under en skräpinsamling. Om en betydande mängd processtid spenderas i en skräpinsamling är antalet samlingar för ofta eller samlingen varar för länge. En ökad allokeringshastighet för objekt på den hanterade heapen gör att skräpinsamling sker oftare. Om du minskar allokeringsfrekvensen minskar frekvensen för skräpsamlingar.

Du kan övervaka allokeringsfrekvensen med hjälp av prestandaräknaren Allocated Bytes/second . Mer information finns i Prestandaräknare i .NET.

Varaktigheten för en samling är främst en faktor för antalet objekt som överlever efter allokeringen. Skräpinsamlaren måste gå igenom en stor mängd minne om många objekt återstår att samla in. Arbetet med att komprimera de överlevande är tidskrävande. För att avgöra hur många objekt som hanterades under en samling anger du en brytpunkt i felsökningsprogrammet i slutet av en skräpinsamling för en angiven generation.

Prestandakontroller
Kontrollera om hög CPU-användning orsakas av skräpinsamling.
Ange en brytpunkt i slutet av skräpinsamlingen.

Felsökningsriktlinjer

I det här avsnittet beskrivs riktlinjer som du bör tänka på när du påbörjar dina undersökningar.

Skräpinsamling för arbetsstation eller server

Kontrollera om du använder rätt typ av skräpinsamling. Om programmet använder flera trådar och objektinstanser använder du skräpinsamling för servern i stället för arbetsstationsskräpinsamling. Server skräpinsamling fungerar på flera trådar, medan arbetsstation skräpinsamling kräver flera instanser av ett program för att köra sina egna skräpinsamling trådar och konkurrera om CPU-tid.

Ett program som har låg belastning och som utför uppgifter sällan i bakgrunden, till exempel en tjänst, kan använda arbetsstationens skräpinsamling med samtidig skräpinsamling inaktiverad.

När den hanterade heapstorleken ska mätas

Om du inte använder en profilerare måste du upprätta ett konsekvent mätmönster för att effektivt diagnostisera prestandaproblem. Överväg följande punkter för att upprätta ett schema:

  • Om du mäter efter en skräpinsamling i generation 2 blir hela den hanterade heapen fri från skräp (döda objekt).
  • Om du mäter direkt efter en skräpinsamling av generation 0 samlas inte objekten i generation 1 och 2 in ännu.
  • Om du mäter omedelbart före en skräpinsamling mäter du så mycket allokering som möjligt innan skräpinsamlingen startar.
  • Att mäta under en skräpinsamling är problematiskt eftersom datastrukturerna för skräpinsamlaren inte är i ett giltigt tillstånd för bläddering och kanske inte kan ge dig fullständiga resultat. Det här är avsiktligt.
  • När du använder arbetsstationens skräpinsamling med samtidig skräpinsamling komprimeras inte de återvunna objekten, så heapstorleken kan vara samma eller större (fragmentering kan göra att den verkar vara större).
  • Samtidig skräpinsamling på generation 2 fördröjs när den fysiska minnesbelastningen är för hög.

Följande procedur beskriver hur du anger en brytpunkt så att du kan mäta den hanterade heapen.

Så här anger du en brytpunkt i slutet av skräpinsamlingen

  • I WinDbg med SOS-felsökningstillägget inläst anger du följande kommando:

    bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"

    Ange GcCondemnedGeneration till önskad generation. Det här kommandot kräver privata symboler.

    Det här kommandot tvingar fram en paus om RestartEE körs efter att objekt av andra generationen har tagits bort för skräpinsamling.

    I serverns skräpinsamling anropar RestartEEbara en tråd , så brytpunkten sker bara en gång under en skräpinsamling i generation 2.

Procedurer för prestandakontroll

I det här avsnittet beskrivs följande procedurer för att isolera orsaken till ditt prestandaproblem:

Så här avgör du om problemet orsakas av skräpinsamling

  • Granska följande två minnesprestandaräknare:

    • % tid i GC. Visar procentandelen förfluten tid som spenderades på att utföra en skräpinsamling efter den senaste skräpinsamlingscykeln. Använd den här räknaren för att avgöra om skräpinsamlaren lägger för mycket tid på att göra hanterat heaputrymme tillgängligt. Om tiden i skräpinsamlingen är relativt låg kan det tyda på ett resursproblem utanför den hanterade heapen. Den här räknaren kanske inte är korrekt när samtidig eller bakgrunds skräpinsamling ingår.

    • # Totalt antal incheckade byte. Visar mängden virtuellt minne som för närvarande har checkats in av skräpinsamlaren. Använd den här räknaren för att avgöra om det minne som förbrukas av skräpinsamlaren är en alltför stor del av det minne som programmet använder.

    De flesta minnesprestandaräknare uppdateras i slutet av varje skräpinsamling. Därför kanske de inte återspeglar de aktuella villkor som du vill ha information om.

Så här avgör du om undantaget för out-of-memory hanteras

  1. I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningsprogrammet inläst anger du kommandot för utskriftsfel (pe):

    !pe

    Om undantaget hanteras OutOfMemoryException visas som undantagstyp, som visas i följande exempel.

    Exception object: 39594518
    Exception type: System.OutOfMemoryException
    Message: <none>
    InnerException: <none>
    StackTrace (generated):
    
  2. Om utdata inte anger något undantag måste du ta reda på vilken tråd undantaget för out-of-memory är från. Ange följande kommando i felsökningsprogrammet för att visa alla trådar med sina anropsstackar:

    ~\*kb

    Tråden med stacken som har undantagsanrop indikeras av RaiseTheException argumentet. Det här är det hanterade undantagsobjektet.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
    
  3. Du kan använda följande kommando för att dumpa kapslade undantag.

    !pe -nested

    Om du inte hittar några undantag kommer undantaget för out-of-memory från ohanterad kod.

Så här avgör du hur mycket virtuellt minne som kan reserveras

  • I WinDbg med SOS-felsökningstillägget inläst anger du följande kommando för att hämta den största kostnadsfria regionen:

    !address -summary

    Den största kostnadsfria regionen visas enligt följande utdata.

    Largest free region: Base 54000000 - Size 0003A980
    

    I det här exemplet är storleken på den största kostnadsfria regionen cirka 24 000 KB (3A980 i hexadecimal). Den här regionen är mycket mindre än vad skräpinsamlaren behöver för ett segment.

    -Eller-

  • vmstat Använd kommandot:

    !vmstat

    Den största kostnadsfria regionen är det största värdet i kolumnen MAXIMUM, enligt följande utdata.

    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
    

För att avgöra om det finns tillräckligt med fysiskt minne

  1. Starta Aktivitetshanteraren för Windows.

  2. På fliken Performance tittar du på det bekräftade värdet. (I Windows 7 tittar du på Commit (KB) i System group.)

    Total Om är nära Limithar du ont om fysiskt minne.

Så här avgör du hur mycket minne den hanterade heapen förbinder

  • Använd minnesprestandaräknaren # Total committed bytes för att hämta antalet byte som den hanterade heapen genomför. Skräpinsamlaren checkar in segment i ett segment efter behov, inte alla på samma gång.

    Kommentar

    Använd inte prestandaräknaren # Bytes in all Heaps eftersom den inte representerar den faktiska minnesanvändningen av den hanterade heapen. Storleken på en generation ingår i det här värdet och är i själva verket dess tröskelstorlek, dvs. den storlek som inducerar en skräpinsamling om genereringen är fylld med objekt. Därför är det här värdet vanligtvis noll.

Så här avgör du hur mycket minne den hanterade heapen reserverar

  • # Total reserved bytes Använd minnesprestandaräknaren.

    Skräpinsamlaren reserverar minne i segment och du kan avgöra var ett segment börjar med hjälp eeheap av kommandot .

    Viktigt!

    Även om du kan fastställa mängden minne som skräpinsamlaren allokerar för varje segment, är segmentstorleken implementeringsspecifik och kan ändras när som helst, inklusive i periodiska uppdateringar. Din app bör aldrig göra antaganden om eller vara beroende av en viss segmentstorlek och bör inte heller försöka konfigurera mängden minne som är tillgängligt för segmentallokeringar.

  • I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningstillägget inläst anger du följande kommando:

    !eeheap -gc

    Resultatet är som följer.

    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)
    

    De adresser som anges av "segment" är startadresserna för segmenten.

Så här fastställer du stora objekt i generation 2

  • I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningstillägget inläst anger du följande kommando:

    !dumpheap –stat

    Om den hanterade heapen är stor dumpheap kan det ta ett tag att slutföra.

    Du kan börja analysera från de sista raderna i utdata eftersom de listar de objekt som använder mest utrymme. Till exempel:

    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
    

    Det sista objektet i listan är en sträng och upptar mest utrymme. Du kan undersöka programmet för att se hur dina strängobjekt kan optimeras. Om du vill se strängar mellan 150 och 200 byte anger du följande:

    !dumpheap -type System.String -min 150 -max 200

    Ett exempel på resultatet är följande.

    Address  MT           Size  Gen
    1875d2c0 790fa3e0      152    2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11
    …
    

    Det kan vara effektivare att använda ett heltal i stället för en sträng för ett ID. Om samma sträng upprepas tusentals gånger bör du överväga strängpraktik. Mer information om strängpraktik finns i referensavsnittet String.Intern för metoden.

Så här fastställer du referenser till objekt

  • I WinDbg med SOS-felsökningstillägget inläst anger du följande kommando för att visa referenser till objekt:

    !gcroot

    -Eller-

  • Ta med adressen för att fastställa referenserna för ett specifikt objekt:

    !gcroot 1c37b2ac

    Rötter som finns på staplar kan vara falska positiva identifieringar. Mer information finns i kommandot !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
    

    Det gcroot kan ta lång tid att slutföra kommandot. Alla objekt som inte frigörs av skräpinsamling är ett live-objekt. Det innebär att en viss rot direkt eller indirekt håller i objektet, så gcroot bör returnera sökvägsinformation till objektet. Du bör undersöka graferna som returneras och se varför dessa objekt fortfarande refereras.

Så här avgör du om en slutförare har körts

  • Kör ett testprogram som innehåller följande kod:

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    

    Om testet löser problemet innebär det att skräpinsamlaren inte återtog objekt eftersom finalizers för dessa objekt hade pausats. Metoden GC.WaitForPendingFinalizers gör det möjligt för slutförarna att slutföra sina uppgifter och åtgärdar problemet.

För att avgöra om det finns objekt som väntar på att slutföras

  1. I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningstillägget inläst anger du följande kommando:

    !finalizequeue

    Titta på antalet objekt som är redo för slutförande. Om antalet är högt måste du undersöka varför dessa finalizers inte kan utvecklas alls eller inte kan utvecklas tillräckligt snabbt.

  2. Om du vill hämta utdata från trådar anger du följande kommando:

    !threads -special

    Det här kommandot innehåller utdata, till exempel följande.

       OSID     Special thread type
    2    cd0    DbgHelper
    3    c18    Finalizer
    4    df0    GC SuspendEE
    

    Finalizer-tråden anger vilken finalator, om någon, som körs för närvarande. När en finaliserartråd inte kör några finalizers väntar den på att en händelse ska instruera den att utföra sitt arbete. För det mesta kommer du att se finalizertråden i det här tillståndet eftersom den körs på THREAD_HIGHEST_PRIORITY och är tänkt att slutföra körningen av finalizers, om någon, mycket snabbt.

Så här fastställer du mängden ledigt utrymme i den hanterade heapen

  • I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningstillägget inläst anger du följande kommando:

    !dumpheap -type Free -stat

    Det här kommandot visar den totala storleken på alla kostnadsfria objekt på den hanterade heapen, som du ser i följande exempel.

    total 230 objects
    Statistics:
          MT    Count    TotalSize Class Name
    00152b18      230     40958584      Free
    Total 230 objects
    
  • Om du vill fastställa det lediga utrymmet i generation 0 anger du följande kommando för information om minnesförbrukning efter generation:

    !eeheap -gc

    Det här kommandot visar utdata som liknar följande. Den sista raden visar det tillfälliga segmentet.

    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)
    
  • Beräkna det utrymme som används av generation 0:

    ? 49e05d04-0x49521f8c

    Resultatet är som följer. Generation 0 är cirka 9 MB.

    Evaluate expression: 9321848 = 008e3d78
    
  • Följande kommando dumpar det lediga utrymmet inom generation 0-intervallet:

    !dumpheap -type Free -stat 0x49521f8c 49e05d04

    Resultatet är som följer.

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

    Dessa utdata visar att generering 0-delen av heapen använder 9 MB utrymme för objekt och har 7 MB ledigt. Den här analysen visar i vilken utsträckning generation 0 bidrar till fragmentering. Den här mängden heapanvändning bör diskonteras från det totala beloppet som orsak till fragmentering av långsiktiga objekt.

Så här fastställer du antalet fästa objekt

  • I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningstillägget inläst anger du följande kommando:

    !gchandles

    Den statistik som visas innehåller antalet fästa handtag, vilket visas i följande exempel.

    GC Handle Statistics:
    Strong Handles:      29
    Pinned Handles:      10
    

Så här avgör du hur lång tid en skräpinsamling tar

  • % Time in GC Granska minnesprestandaräknaren.

    Värdet beräknas med hjälp av en exempelintervalltid. Eftersom räknarna uppdateras i slutet av varje skräpinsamling har det aktuella exemplet samma värde som föregående exempel om inga samlingar inträffade under intervallet.

    Insamlingstiden erhålls genom att multiplicera exempelintervalltiden med procentvärdet.

    Följande data visar fyra samplingsintervall på två sekunder, för en 8-sekunders studie. Kolumnerna Gen0, Gen1och Gen2 visar det totala antalet skräpsamlingar som har slutförts i slutet av intervallet för den generationen.

    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
    

    Den här informationen visar inte när skräpinsamlingen inträffade, men du kan fastställa antalet skräpsamlingar som inträffade under ett tidsintervall. Om vi antar det värsta fallet avslutades den tionde generationens skräpinsamling i början av det andra intervallet och den elfte generationens skräpinsamling av generation 0 avslutades i slutet av det tredje intervallet. Tiden mellan slutet av den tionde och slutet av den elfte skräpinsamlingen är cirka 2 sekunder och prestandaräknaren visar 3 %, så varaktigheten för den elfte generationens skräpinsamling var (2 sekunder * 3 % = 60 ms).

    I nästa exempel finns det fem intervall.

    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
    

    Den andra generationens skräpinsamling startade under det fjärde intervallet och avslutades med det femte intervallet. Om vi antar det värsta fallet var den sista skräpinsamlingen för en generation 0-samling som avslutades i början av det tredje intervallet, och skräpinsamlingen av generation 2 avslutades i slutet av det femte intervallet. Därför är tiden mellan slutet av genereringen 0 skräpinsamling och slutet av generation 2 skräpinsamling 4 sekunder. Eftersom räknaren % Time in GC är 20 %, är den maximala tiden som skräpinsamlingen i generation 2 kan ha tagit (4 sekunder * 20 % = 800 ms).

  • Du kan också fastställa längden på en skräpinsamling med ETW-händelser för skräpinsamling och analysera informationen för att fastställa hur lång tid skräpinsamlingen tar.

    Följande data visar till exempel en händelsesekvens som inträffade under en icke-samtidig skräpinsamling.

    Timestamp    Event name
    513052        GCSuspendEEBegin_V1
    513078        GCSuspendEEEnd
    513090        GCStart_V1
    517890        GCEnd_V1
    517894        GCHeapStats
    517897        GCRestartEEBegin
    517918        GCRestartEEEnd
    

    Pausa den hanterade tråden tog 26us (GCSuspendEEEndGCSuspendEEBegin_V1).

    Den faktiska skräpinsamlingen tog 4,8 ms (GCEnd_V1GCStart_V1).

    Att återuppta de hanterade trådarna tog 21us (GCRestartEEEndGCRestartEEBegin).

    Följande utdata är ett exempel för skräpinsamling i bakgrunden och innehåller fälten process, tråd och händelse. (Alla data visas inte.)

    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
    

    Händelsen GCStart_V1 på 42504816 anger att det här är en skräpinsamling i bakgrunden eftersom det sista fältet är 1. Detta blir skräpinsamling Nr. 102019.

    Händelsen GCStart inträffar eftersom det finns ett behov av en tillfällig skräpinsamling innan du startar en skräpinsamling i bakgrunden. Detta blir skräpinsamling nr 102020.

    Vid 42514170 slutförs skräpinsamling nr 102020. De hanterade trådarna startas om nu. Detta har slutförts på tråd 4372, som utlöste den här skräpinsamlingen i bakgrunden.

    På tråd 4744 uppstår en fjädring. Det här är den enda gången som skräpinsamlingen i bakgrunden måste pausa hanterade trådar. Den här varaktigheten är cirka 99 ms ((63784407-63685394)/1000).

    Händelsen GCEnd för skräpinsamlingen i bakgrunden är på 89931423. Det innebär att skräpinsamlingen i bakgrunden varade i cirka 47 sekunder ((89931423-42504816)/1000).

    När de hanterade trådarna körs kan du se hur många tillfälliga skräpsamlingar som helst.

Så här avgör du vad som utlöste en skräpinsamling

  • I Felsökningsprogrammet för WinDbg eller Visual Studio med SOS-felsökningsprogrammet inläst anger du följande kommando för att visa alla trådar med sina anropsstackar:

    ~*Kb

    Det här kommandot visar utdata som liknar följande.

    0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect
    0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4
    0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
    

    Om skräpinsamlingen orsakades av ett meddelande om lågt minne från operativsystemet är anropsstacken liknande, förutom att tråden är finalizer-tråden. Finalizer-tråden hämtar ett asynkront meddelande om lågt minne och inducerar skräpinsamlingen.

    Om skräpinsamlingen orsakades av minnesallokering visas stacken på följande sätt:

    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
    

    En just-in-time-hjälp (JIT_New*) anropar GCHeap::GarbageCollectGenerationså småningom . Om du fastställer att skräpsamlingar av generation 2 orsakas av allokeringar måste du bestämma vilka objekt som samlas in av en skräpinsamling av generation 2 och hur de ska undvikas. Du vill alltså fastställa skillnaden mellan början och slutet av en skräpinsamling av generation 2 och de objekt som orsakade samlingen av generation 2.

    Ange till exempel följande kommando i felsökningsprogrammet för att visa början av en samling i generation 2:

    !dumpheap –stat

    Exempelutdata (förkortat för att visa de objekt som använder mest utrymme):

    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
    

    Upprepa kommandot i slutet av generation 2:

    !dumpheap –stat

    Exempelutdata (förkortat för att visa de objekt som använder mest utrymme):

    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
    

    Objekten double[] försvann från slutet av utdata, vilket innebär att de samlades in. Dessa objekt står för cirka 70 MB. De återstående objekten ändrades inte mycket. Därför var dessa double[] objekt orsaken till att den här skräpinsamlingen av generation 2 inträffade. Nästa steg är att avgöra varför objekten double[] finns där och varför de dog. Du kan fråga kodutvecklaren var dessa objekt kommer ifrån, eller så kan du använda gcroot kommandot .

Så här avgör du om hög CPU-användning orsakas av skräpinsamling

  • Korrelera minnesprestandaräknarens % Time in GC värde med processtiden.

    Om värdet % Time in GC ökar samtidigt som processtiden orsakar skräpinsamling en hög CPU-användning. Annars profilerar du programmet för att ta reda på var den höga användningen sker.

Se även