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.
- Ett undantag från slutminnet utlöses
- Processen använder för mycket minne
- Skräpinsamlaren återtar inte objekt tillräckligt snabbt
- Den hanterade heapen är för fragmenterad
- Pauser i skräpinsamlingen är för långa
- Generation 0 är för stor
- CPU-användningen under en skräpinsamling är för hög
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.
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
RestartEE
bara 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:
- Ta reda på om problemet orsakas av skräpinsamling.
- 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.
- 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.
- Avgör om en finalizer har körts.
- Avgör om det finns objekt som väntar på att slutföras.
- Fastställa mängden ledigt utrymme i den hanterade heapen.
- Fastställa antalet fästa objekt.
- Fastställa hur lång tid det tar i en skräpinsamling.
- Ta reda på vad som utlöste en skräpinsamling.
- Avgör om hög CPU-användning orsakas av skräpinsamling.
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
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):
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
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
Starta Aktivitetshanteraren för Windows.
På fliken
Performance
tittar du på det bekräftade värdet. (I Windows 7 tittar du påCommit (KB)
iSystem group
.)Total
Om är näraLimit
har 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
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.
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
,Gen1
ochGen2
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 (
GCSuspendEEEnd
–GCSuspendEEBegin_V1
).Den faktiska skräpinsamlingen tog 4,8 ms (
GCEnd_V1
–GCStart_V1
).Att återuppta de hanterade trådarna tog 21us (
GCRestartEEEnd
–GCRestartEEBegin
).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 är1
. 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*
) anroparGCHeap::GarbageCollectGeneration
så 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 dessadouble[]
objekt orsaken till att den här skräpinsamlingen av generation 2 inträffade. Nästa steg är att avgöra varför objektendouble[]
finns där och varför de dog. Du kan fråga kodutvecklaren var dessa objekt kommer ifrån, eller så kan du användagcroot
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.