Garbagecollection en prestaties

In dit artikel worden problemen beschreven met betrekking tot garbagecollection en geheugengebruik. Hiermee worden problemen opgelost die betrekking hebben op de beheerde heap en wordt uitgelegd hoe u het effect van garbagecollection op uw toepassingen kunt minimaliseren. Elk probleem bevat koppelingen naar procedures die u kunt gebruiken om problemen te onderzoeken.

Hulpprogramma's voor prestatieanalyse

In de volgende secties worden de hulpprogramma's beschreven die beschikbaar zijn voor het onderzoeken van geheugengebruik en problemen met garbagecollection. De procedures die verderop in dit artikel worden beschreven, verwijzen naar deze hulpprogramma's.

Prestatiemeteritems voor geheugen

U kunt prestatiemeteritems gebruiken om prestatiegegevens te verzamelen. Zie Runtimeprofilering voor instructies. De .NET CLR-geheugencategorie prestatiemeteritems, zoals beschreven in Prestatiemeteritems in .NET, bevat informatie over de garbagecollector.

Foutopsporing met SOS

U kunt het Windows Debugger (WinDbg) gebruiken om objecten op de beheerde heap te inspecteren.

Als u WinDbg wilt installeren, installeert u Hulpprogramma's voor foutopsporing voor Windows via de pagina Hulpprogramma's voor foutopsporing downloaden.

ETW-gebeurtenissen garbagecollection

Gebeurtenistracering voor Windows (ETW) is een traceringssysteem dat een aanvulling vormt op de profilerings- en foutopsporingsondersteuning van .NET. Vanaf .NET Framework 4 bevatten ETW-gebeurtenissen voor garbagecollection nuttige informatie voor het analyseren van de beheerde heap vanuit een statistisch oogpunt. De gebeurtenis, die wordt gegenereerd wanneer een garbagecollection zich op het punt bevindt, bevat bijvoorbeeld GCStart_V1 de volgende informatie:

  • Welke generatie objecten worden verzameld.
  • Wat de garbagecollection heeft geactiveerd.
  • Type garbagecollection (gelijktijdig of niet gelijktijdig).

ETW-gebeurtenislogboekregistratie is efficiënt en maskert geen prestatieproblemen met betrekking tot garbagecollection. Een proces kan eigen gebeurtenissen bieden in combinatie met ETW-gebeurtenissen. Bij het logboek kunnen zowel de gebeurtenissen van de toepassing als de garbagecollectiongebeurtenissen worden gecorreleerd om te bepalen hoe en wanneer er heap-problemen optreden. Een servertoepassing kan bijvoorbeeld gebeurtenissen aan het begin en einde van een clientaanvraag leveren.

De profilerings-API

De clr-profileringsinterfaces (Common Language Runtime) bieden gedetailleerde informatie over de objecten die zijn beïnvloed tijdens de garbagecollection. Een profiler kan worden gewaarschuwd wanneer een garbagecollection wordt gestart en eindigt. Het kan rapporten geven over de objecten op de beheerde heap, inclusief een identificatie van objecten in elke generatie. Zie Profileringsoverzicht voor meer informatie.

Profilers kunnen uitgebreide informatie bieden. Complexe profilers kunnen het gedrag van een toepassing echter mogelijk wijzigen.

Resourcebewaking van toepassingsdomein

Vanaf .NET Framework 4 kan ARM (Application Domain Resource Monitoring) hosts het CPU- en geheugengebruik per toepassingsdomein bewaken. Zie Toepassingsdomeinresourcebewaking voor meer informatie.

Prestatieproblemen oplossen

De eerste stap is om te bepalen of het probleem daadwerkelijk garbagecollection is. Als u vaststelt dat dit het is, selecteert u in de volgende lijst om het probleem op te lossen.

Probleem: Er wordt een uitzondering met onvoldoende geheugen gegenereerd

Er zijn twee legitieme gevallen voor een gegooid OutOfMemoryException :

  • Onvoldoende virtueel geheugen.

    De garbagecollector wijst geheugen van het systeem toe in segmenten van een vooraf bepaalde grootte. Als voor een toewijzing een extra segment is vereist, maar er geen aaneengesloten vrije blok overblijft in de virtuele geheugenruimte van het proces, mislukt de toewijzing voor de beheerde heap.

  • Er is onvoldoende fysiek geheugen om toe te wijzen.

Prestatiecontroles
Bepaal of de uitzondering buiten het geheugen wordt beheerd.
Bepaal hoeveel virtueel geheugen kan worden gereserveerd.
Bepaal of er voldoende fysiek geheugen is.

Als u vaststelt dat de uitzondering niet legitiem is, neemt u contact op met de microsoft-klantenservice en -ondersteuning met de volgende informatie:

  • De stack met de beheerde uitzondering voor onvoldoende geheugen.
  • Volledige geheugendump.
  • Gegevens die aantonen dat het geen legitieme uitzondering voor onvoldoende geheugen is, inclusief gegevens die laten zien dat virtueel of fysiek geheugen geen probleem is.

Probleem: het proces gebruikt te veel geheugen

Een veelvoorkomende aanname is dat het geheugengebruik wordt weergegeven op het tabblad Prestaties van Windows Taakbeheer kan aangeven wanneer er te veel geheugen wordt gebruikt. Deze weergave heeft echter betrekking op de werkset; het biedt geen informatie over het gebruik van virtueel geheugen.

Als u vaststelt dat het probleem wordt veroorzaakt door de beheerde heap, moet u de beheerde heap in de loop van de tijd meten om patronen te bepalen.

Als u vaststelt dat het probleem niet wordt veroorzaakt door de beheerde heap, moet u systeemeigen foutopsporing gebruiken.

Prestatiecontroles
Bepaal hoeveel virtueel geheugen kan worden gereserveerd.
Bepaal hoeveel geheugen de beheerde heap doorvoert.
Bepaal hoeveel geheugen de beheerde heap reserveert.
Bepaal grote objecten in generatie 2.
Verwijzingen naar objecten bepalen.

Probleem: De garbagecollection maakt objecten niet snel genoeg vrij

Wanneer het lijkt alsof objecten niet worden vrijgemaakt zoals verwacht voor garbagecollection, moet u bepalen of er sterke verwijzingen naar deze objecten zijn.

U kunt dit probleem ook tegenkomen als er geen garbagecollection is voor de generatie die een dood object bevat, wat aangeeft dat de finalizer voor het dode object niet is uitgevoerd. Dit is bijvoorbeeld mogelijk wanneer u een STA-toepassing (Single Threaded Apartment) uitvoert en de thread die de finalizer-wachtrij biedt, deze niet kan aanroepen.

Prestatiecontroles
Controleer verwijzingen naar objecten.
Bepaal of er een finalizer is uitgevoerd.
Bepaal of er objecten zijn die moeten worden voltooid.

Probleem: de beheerde heap is te gefragmenteerd

Het fragmentatieniveau wordt berekend als de verhouding tussen vrije ruimte en het totale toegewezen geheugen voor de generatie. Voor generatie 2 is een acceptabel fragmentatieniveau niet meer dan 20%. Omdat generatie 2 erg groot kan worden, is de verhouding van fragmentatie belangrijker dan de absolute waarde.

Veel vrije ruimte in generatie 0 is geen probleem omdat dit de generatie is waar nieuwe objecten worden toegewezen.

Fragmentatie vindt altijd plaats in de grote object-heap omdat deze niet wordt gecomprimeerd. Vrije objecten die aangrenzend zijn, worden natuurlijk samengevouwen in één ruimte om te voldoen aan grote aanvragen voor objecttoewijzing.

Fragmentatie kan een probleem worden in generatie 1 en generatie 2. Als deze generaties een grote hoeveelheid vrije ruimte hebben na een garbagecollection, moet het objectgebruik van een toepassing mogelijk worden gewijzigd en moet u overwegen de levensduur van objecten op lange termijn opnieuw te evalueren.

Overmatige vastmaken van objecten kan fragmentatie verhogen. Als fragmentatie hoog is, kunnen er te veel objecten zijn vastgemaakt.

Als fragmentatie van het virtuele geheugen verhindert dat de garbagecollector segmenten toevoegt, kunnen de oorzaken een van de volgende oorzaken hebben:

  • Vaak laden en lossen van veel kleine assembly's.

  • Er zijn te veel verwijzingen naar COM-objecten bij het samenwerken met onbeheerde code.

  • Het maken van grote tijdelijke objecten, waardoor de grote object-heap vaak heap-segmenten toewijst en vrijkomt.

    Bij het hosten van de CLR kan een toepassing aanvragen dat de garbagecollector de segmenten behoudt. Dit vermindert de frequentie van segmenttoewijzingen. Dit wordt bereikt met behulp van de vlag STARTUP_HOARD_GC_VM in de opsomming STARTUP_FLAGS.

Prestatiecontroles
Bepaal de hoeveelheid vrije ruimte in de beheerde heap.
Het aantal vastgemaakte objecten bepalen.

Als u denkt dat er geen legitieme oorzaak is voor de fragmentatie, neemt u contact op met de klantenservice en de ondersteuning van Microsoft.

Probleem: Garbagecollection pauzes zijn te lang

Garbagecollection werkt in zachte realtime, dus een toepassing moet enkele pauzes kunnen tolereren. Een criterium voor zachte realtime is dat 95% van de bewerkingen op tijd moet worden voltooid.

In gelijktijdige garbagecollection mogen beheerde threads worden uitgevoerd tijdens een verzameling, wat betekent dat pauzes zeer minimaal zijn.

Kortstondige garbageverzamelingen (generaties 0 en 1) duren slechts een paar milliseconden, dus het verlagen van pauzes is meestal niet haalbaar. U kunt de pauzes in verzamelingen van de tweede generatie echter verminderen door het patroon van toewijzingsaanvragen door een toepassing te wijzigen.

Een andere, nauwkeurigere methode is het gebruik van ETW-gebeurtenissen voor garbagecollection. U vindt de tijdsinstellingen voor verzamelingen door de tijdstempelverschillen voor een reeks gebeurtenissen toe te voegen. De hele verzamelingsreeks omvat schorsing van de uitvoeringsengine, de garbagecollection zelf en de hervatting van de uitvoeringsengine.

U kunt garbagecollectionmeldingen gebruiken om te bepalen of een server op het punt staat een verzameling van de tweede generatie te hebben en of het opnieuw omleiden van aanvragen naar een andere server kan eventuele problemen met pauzes vereenvoudigen.

Prestatiecontroles
Bepaal de tijdsduur in een garbagecollection.
Bepaal wat een garbagecollection heeft veroorzaakt.

Probleem: Generatie 0 is te groot

Generatie 0 heeft waarschijnlijk een groter aantal objecten op een 64-bits systeem, vooral wanneer u de garbagecollection van de server gebruikt in plaats van garbagecollection van werkstation. Dit komt doordat de drempelwaarde voor het activeren van een garbagecollection van de 0 generatie hoger is in deze omgevingen en verzamelingen van generatie 0 veel groter kunnen worden. De prestaties worden verbeterd wanneer een toepassing meer geheugen toewijst voordat een garbagecollection wordt geactiveerd.

Probleem: CPU-gebruik tijdens een garbagecollection is te hoog

HET CPU-gebruik is hoog tijdens een garbagecollection. Als een aanzienlijke hoeveelheid procestijd wordt besteed aan een garbagecollection, is het aantal verzamelingen te vaak of duurt de verzameling te lang. Een verhoogde toewijzingssnelheid van objecten op de beheerde heap zorgt ervoor dat garbagecollection vaker voorkomt. Als u de toewijzingssnelheid verlaagt, wordt de frequentie van garbagecollectionen verminderd.

U kunt toewijzingssnelheden bewaken met behulp van het Allocated Bytes/second prestatiemeteritem. Zie Prestatiemeteritems in .NET voor meer informatie.

De duur van een verzameling is voornamelijk een factor van het aantal objecten dat na de toewijzing overleeft. De garbagecollector moet een grote hoeveelheid geheugen doorlopen als veel objecten moeten worden verzameld. Het werk om de overlevenden te comprimeren is tijdrovend. Als u wilt bepalen hoeveel objecten tijdens een verzameling zijn verwerkt, stelt u een onderbrekingspunt in het foutopsporingsprogramma aan het einde van een garbagecollection in voor een opgegeven generatie.

Prestatiecontroles
Bepaal of een hoog CPU-gebruik wordt veroorzaakt door garbagecollection.
Stel een onderbrekingspunt in aan het einde van de garbagecollection.

Richtlijnen voor probleemoplossing

In deze sectie worden richtlijnen beschreven waarmee u rekening moet houden wanneer u begint met uw onderzoeken.

Werkstation of server garbagecollection

Bepaal of u het juiste type garbagecollection gebruikt. Als uw toepassing meerdere threads en objectexemplaren gebruikt, gebruikt u de garbagecollection van de server in plaats van garbagecollection van werkstations. De garbagecollection van de server werkt op meerdere threads, terwijl de garbagecollection van het werkstation meerdere exemplaren van een toepassing nodig heeft om hun eigen threads voor garbagecollection uit te voeren en te concurreren voor CPU-tijd.

Een toepassing met een lage belasting en die taken niet vaak op de achtergrond uitvoert, zoals een service, kan werkstation garbagecollection gebruiken waarbij gelijktijdige garbagecollection is uitgeschakeld.

Wanneer moet u de grootte van de beheerde heap meten

Tenzij u een profiler gebruikt, moet u een consistent meetpatroon instellen om prestatieproblemen effectief te diagnosticeren. Houd rekening met de volgende punten om een planning op te stellen:

  • Als u meet na een afvalverzameling van de tweede generatie, is de hele beheerde heap vrij van garbage (dode objecten).
  • Als u onmiddellijk na een generatie 0 garbagecollection meet, worden de objecten in generaties 1 en 2 nog niet verzameld.
  • Als u direct vóór een garbagecollection meet, meet u zoveel mogelijk toewijzing voordat de garbagecollection wordt gestart.
  • Meten tijdens een garbagecollection is problematisch, omdat de gegevensstructuren van de garbagecollector niet geldig zijn voor doorkruising en u mogelijk niet de volledige resultaten kunnen geven. Dit is standaard.
  • Wanneer u werkstation garbagecollection gebruikt met gelijktijdige garbagecollection, worden de vrijgemaakte objecten niet gecomprimeerd, zodat de heapgrootte hetzelfde of groter kan zijn (fragmentatie kan het lijken groter te zijn).
  • Gelijktijdige garbagecollection op generatie 2 wordt vertraagd wanneer de belasting van het fysieke geheugen te hoog is.

In de volgende procedure wordt beschreven hoe u een onderbrekingspunt instelt, zodat u de beheerde heap kunt meten.

Een onderbrekingspunt instellen aan het einde van de garbagecollection

  • Voer in WinDbg met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

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

    Ingesteld GcCondemnedGeneration op de gewenste generatie. Voor deze opdracht zijn persoonlijke symbolen vereist.

    Met deze opdracht wordt een onderbreking afgedwongen als RestartEE deze wordt uitgevoerd nadat de tweede generatie objecten zijn vrijgemaakt voor garbagecollection.

    In de garbagecollection van de server, slechts één thread aanroepen RestartEE, dus het onderbrekingspunt vindt slechts één keer plaats tijdens een garbagecollection van de tweede generatie.

Procedures voor prestatiecontrole

In deze sectie worden de volgende procedures beschreven om de oorzaak van uw prestatieprobleem te isoleren:

Bepalen of het probleem wordt veroorzaakt door garbagecollection

  • Bekijk de volgende twee prestatiemeteritems voor geheugen:

    • % tijd in GC. Geeft het percentage verstreken tijd weer dat is besteed aan het uitvoeren van een garbagecollection na de laatste garbagecollectioncyclus. Gebruik deze teller om te bepalen of de garbagecollector te veel tijd besteedt om beheerde heapruimte beschikbaar te maken. Als de tijd die is besteed aan garbagecollection relatief laag is, kan dit duiden op een resourceprobleem buiten de beheerde heap. Deze teller is mogelijk niet nauwkeurig wanneer gelijktijdige of achtergrond garbagecollection betrokken is.

    • # Totaal aantal doorgevoerde bytes. Geeft de hoeveelheid virtueel geheugen weer die momenteel is doorgevoerd door de garbagecollector. Gebruik deze teller om te bepalen of het geheugen dat door de garbagecollector wordt verbruikt, een overmatig deel is van het geheugen dat door uw toepassing wordt gebruikt.

    De meeste prestatiemeteritems voor geheugen worden aan het einde van elke garbagecollection bijgewerkt. Daarom geven ze mogelijk niet de huidige voorwaarden weer waarover u informatie wilt.

Bepalen of de uitzondering buiten het geheugen wordt beheerd

  1. Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsextensie geladen de opdracht afdrukuitzondering (pe) in:

    !pe

    Als de uitzondering wordt beheerd, OutOfMemoryException wordt deze weergegeven als het uitzonderingstype, zoals wordt weergegeven in het volgende voorbeeld.

    Exception object: 39594518
    Exception type: System.OutOfMemoryException
    Message: <none>
    InnerException: <none>
    StackTrace (generated):
    
  2. Als de uitvoer geen uitzondering opgeeft, moet u bepalen van welke thread de uitzondering buiten het geheugen afkomstig is. Voer de volgende opdracht in het foutopsporingsprogramma in om alle threads met hun aanroepstacks weer te geven:

    ~\*kb

    De thread met de stack met uitzonderingsoproepen wordt aangegeven door het RaiseTheException argument. Dit is het beheerde uitzonderingsobject.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
    
  3. U kunt de volgende opdracht gebruiken om geneste uitzonderingen te dumpen.

    !pe -nested

    Als u geen uitzonderingen vindt, is de uitzondering buiten het geheugen afkomstig van niet-beheerde code.

Bepalen hoeveel virtueel geheugen kan worden gereserveerd

  • Voer in WinDbg met de SOS-foutopsporingsprogramma-extensie geladen de volgende opdracht in om de grootste gratis regio op te halen:

    !address -summary

    De grootste vrije regio wordt weergegeven zoals weergegeven in de volgende uitvoer.

    Largest free region: Base 54000000 - Size 0003A980
    

    In dit voorbeeld is de grootte van de grootste vrije regio ongeveer 24000 KB (3A980 in hexadecimaal). Deze regio is veel kleiner dan wat de garbagecollector nodig heeft voor een segment.

    -Of-

  • Gebruik de vmstat opdracht:

    !vmstat

    De grootste vrije regio is de grootste waarde in de kolom MAXIMUM, zoals wordt weergegeven in de volgende uitvoer.

    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
    

Bepalen of er voldoende fysiek geheugen is

  1. Start Windows Taakbeheer.

  2. Kijk op het Performance tabblad naar de vastgelegde waarde. (In Windows 7 bekijkt Commit (KB) u in de System group.)

    Als de verbinding Total zich dicht bij het Limitbestand bevindt, hebt u weinig fysiek geheugen.

Bepalen hoeveel geheugen de beheerde heap doorvoert

  • Gebruik het # Total committed bytes prestatiemeteritem voor geheugen om het aantal bytes op te halen dat door de beheerde heap wordt doorgevoerd. De garbagecollector zet segmenten op een segment vast, niet allemaal tegelijk.

    Notitie

    Gebruik de # Bytes in all Heaps prestatiemeteritem niet, omdat dit geen werkelijk geheugengebruik vertegenwoordigt door de beheerde heap. De grootte van een generatie wordt opgenomen in deze waarde en is in feite de drempelwaarde, dat wil gezegd, de grootte die een garbagecollection veroorzaakt als de generatie wordt gevuld met objecten. Daarom is deze waarde meestal nul.

Bepalen hoeveel geheugen de beheerde heap reserveert

  • Gebruik het # Total reserved bytes prestatiemeteritem voor geheugen.

    De garbagecollector reserveert geheugen in segmenten en u kunt bepalen waar een segment begint met behulp van de eeheap opdracht.

    Belangrijk

    Hoewel u kunt bepalen hoeveel geheugen de garbagecollector toewijst voor elk segment, is segmentgrootte specifiek voor implementatie en kan deze op elk gewenst moment worden gewijzigd, ook in periodieke updates. Uw app mag nooit veronderstellingen maken over of afhankelijk zijn van een bepaalde segmentgrootte, en moet ook niet proberen om de hoeveelheid geheugen te configureren die beschikbaar is voor segmenttoewijzingen.

  • Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

    !eeheap -gc

    Dit is het resultaat.

    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 adressen die worden aangegeven door 'segment', zijn de beginadressen van de segmenten.

Grote objecten in generatie 2 bepalen

  • Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

    !dumpheap –stat

    Als de beheerde heap groot is, dumpheap kan het even duren voordat deze klaar is.

    U kunt beginnen met analyseren vanaf de laatste regels van de uitvoer, omdat ze de objecten vermelden die de meeste ruimte gebruiken. Voorbeeld:

    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
    

    Het laatste object dat wordt vermeld, is een tekenreeks en neemt de meeste ruimte in beslag. U kunt uw toepassing onderzoeken om te zien hoe uw tekenreeksobjecten kunnen worden geoptimaliseerd. Als u tekenreeksen tussen 150 en 200 bytes wilt zien, voert u het volgende in:

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

    Een voorbeeld van de resultaten is als volgt.

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

    Het gebruik van een geheel getal in plaats van een tekenreeks voor een id kan efficiënter zijn. Als dezelfde tekenreeks duizenden keren wordt herhaald, kunt u overwegen om tekenreeksen te interneren. Zie het naslagonderwerp voor de String.Intern methode voor meer informatie over het interneren van tekenreeksen.

Verwijzingen naar objecten bepalen

  • Voer in WinDbg met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in om verwijzingen naar objecten weer te geven:

    !gcroot

    -Of-

  • Als u de verwijzingen voor een specifiek object wilt bepalen, neemt u het adres op:

    !gcroot 1c37b2ac

    Wortels gevonden op stapels kunnen fout-positieven zijn. Gebruik de opdracht !help gcrootvoor meer informatie.

    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
    

    Het kan lang duren voordat de gcroot opdracht is voltooid. Een object dat niet door garbagecollection wordt vrijgemaakt, is een live-object. Dit betekent dat een deel van de hoofdmap direct of indirect aan het object vasthoudt, dus gcroot moet padinformatie naar het object worden geretourneerd. U moet de geretourneerde grafieken onderzoeken en zien waarom er nog steeds naar deze objecten wordt verwezen.

Bepalen of een finalizer is uitgevoerd

  • Voer een testprogramma uit dat de volgende code bevat:

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

    Als het probleem met de test wordt opgelost, betekent dit dat de garbagecollector geen objecten terugvorderde, omdat de finalizers voor die objecten waren onderbroken. Met GC.WaitForPendingFinalizers de methode kunnen de finalizers hun taken voltooien en het probleem oplossen.

Bepalen of er objecten zijn die moeten worden voltooid

  1. Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

    !finalizequeue

    Bekijk het aantal objecten dat gereed is voor voltooien. Als het getal hoog is, moet u onderzoeken waarom deze finalizers helemaal niet kunnen worden voortgezet of niet snel genoeg kunnen worden voortgezet.

  2. Voer de volgende opdracht in om een uitvoer van threads op te halen:

    !threads -special

    Deze opdracht biedt uitvoer zoals de volgende.

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

    De finalizer-thread geeft aan welke finalizer, indien aanwezig, momenteel wordt uitgevoerd. Wanneer een finalizer-thread geen finalizers uitvoert, wacht deze op een gebeurtenis om aan te geven dat het werk moet worden uitgevoerd. Meestal ziet u de finalizer-thread in deze status, omdat deze wordt uitgevoerd op THREAD_HIGHEST_PRIORITY en moet worden voltooid met actieve finalizers, indien van toepassing, zeer snel.

De hoeveelheid vrije ruimte in de beheerde heap bepalen

  • Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

    !dumpheap -type Free -stat

    Met deze opdracht wordt de totale grootte van alle vrije objecten op de beheerde heap weergegeven, zoals wordt weergegeven in het volgende voorbeeld.

    total 230 objects
    Statistics:
          MT    Count    TotalSize Class Name
    00152b18      230     40958584      Free
    Total 230 objects
    
  • Als u de vrije ruimte in generatie 0 wilt bepalen, voert u de volgende opdracht in voor informatie over geheugenverbruik per generatie:

    !eeheap -gc

    Met deze opdracht wordt uitvoer weergegeven die vergelijkbaar is met de volgende. De laatste regel toont het kortstondige 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)
    
  • Bereken de ruimte die wordt gebruikt door generatie 0:

    ? 49e05d04-0x49521f8c

    Dit is het resultaat. Generatie 0 is ongeveer 9 MB.

    Evaluate expression: 9321848 = 008e3d78
    
  • Met de volgende opdracht wordt de vrije ruimte binnen het bereik van de 0e generatie gedumpt:

    !dumpheap -type Free -stat 0x49521f8c 49e05d04

    Dit is het resultaat.

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

    Deze uitvoer laat zien dat het 0e deel van de heap 9 MB ruimte voor objecten gebruikt en 7 MB vrij heeft. Deze analyse toont de mate waarin generatie 0 bijdraagt aan fragmentatie. Dit aantal heap-gebruiksrechten moet worden afgetrokken van het totale bedrag als de oorzaak van fragmentatie door objecten op lange termijn.

Het aantal vastgemaakte objecten bepalen

  • Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in:

    !gchandles

    De weergegeven statistieken bevatten het aantal vastgemaakte ingangen, zoals in het volgende voorbeeld wordt weergegeven.

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

De tijdsduur in een garbagecollection bepalen

  • Controleer het prestatiemeteritem van het % Time in GC geheugen.

    De waarde wordt berekend met behulp van een voorbeeldintervaltijd. Omdat de tellers aan het einde van elke garbagecollection worden bijgewerkt, heeft het huidige voorbeeld dezelfde waarde als het vorige voorbeeld als er geen verzamelingen zijn opgetreden tijdens het interval.

    Verzamelingstijd wordt verkregen door de intervaltijd van de steekproef te vermenigvuldigen met de percentagewaarde.

    De volgende gegevens bevatten vier steekproefintervallen van twee seconden, voor een onderzoek van 8 seconden. In Gen0de kolommen , Gen1en Gen2 kolommen wordt het totale aantal garbagecollection's weergegeven dat aan het einde van het interval voor die generatie is voltooid.

    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
    

    Deze informatie wordt niet weergegeven wanneer de garbagecollection is opgetreden, maar u kunt het aantal garbagecollection's bepalen dat in een interval van tijd is opgetreden. Uitgaande van het ergste geval is de tiende generatie 0 garbagecollection voltooid aan het begin van het tweede interval en de elfde generatie 0 garbagecollection voltooid aan het einde van het derde interval. De tijd tussen het einde van de tiende en het einde van de elfde garbagecollection is ongeveer 2 seconden en de prestatiemeteritem toont 3%, dus de duur van de elfde generatie 0 garbagecollection was (2 seconden * 3% = 60 ms).

    In het volgende voorbeeld zijn er vijf intervallen.

    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
    

    De tweede generatie 2 garbagecollection is gestart tijdens het vierde interval en voltooid met het vijfde interval. Uitgaande van het ergste geval was de laatste garbagecollection voor een verzameling van de 0 generatie die aan het begin van het derde interval is voltooid en de garbagecollection van de tweede generatie is voltooid aan het einde van het vijfde interval. Daarom is de tijd tussen het einde van de generatie 0 garbagecollection en het einde van de 2e generatie garbagecollection 4 seconden. Omdat de % Time in GC teller 20% is, is de maximale hoeveelheid tijd die de generatie 2 garbagecollection kan hebben genomen (4 seconden * 20% = 800 ms).

  • U kunt ook de lengte van een garbagecollection bepalen met behulp van ETW-gebeurtenissen voor garbagecollection en de informatie analyseren om de duur van de garbagecollection te bepalen.

    De volgende gegevens bevatten bijvoorbeeld een gebeurtenisreeks die is opgetreden tijdens een niet-gelijktijdige garbagecollection.

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

    Het onderbreken van de beheerde thread duurde 26us (GCSuspendEEEndGCSuspendEEBegin_V1).

    De daadwerkelijke garbagecollection duurde 4,8 ms (GCEnd_V1GCStart_V1).

    Het hervatten van de beheerde threads duurde 21us (GCRestartEEEndGCRestartEEBegin).

    De volgende uitvoer bevat een voorbeeld voor garbagecollection op de achtergrond en bevat de proces-, thread- en gebeurtenisvelden. (Niet alle gegevens worden weergegeven.)

    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
    

    De GCStart_V1 gebeurtenis op 42504816 geeft aan dat dit een achtergrond garbagecollection is, omdat het laatste veld is 1. Dit wordt garbagecollection Nr. 102019.

    De GCStart gebeurtenis treedt op omdat er behoefte is aan een tijdelijke garbagecollection voordat u een achtergrond garbagecollection start. Dit wordt garbagecollection nr. 102020.

    Op 42514170 is garbagecollection nr. 102020 voltooid. De beheerde threads worden op dit moment opnieuw gestart. Dit is voltooid op thread 4372, waardoor deze achtergrond garbagecollection werd geactiveerd.

    Op draad 4744 treedt een ophanging op. Dit is het enige moment waarop de achtergrond garbagecollection beheerde threads moet onderbreken. Deze duur is ongeveer 99 ms ((63784407-63685394)/1000).

    De GCEnd gebeurtenis voor de achtergrond garbagecollection bevindt zich op 89931423. Dit betekent dat de achtergrond garbagecollection ongeveer 47seconds ((89931423-42504816)/1000 duurde.

    Terwijl de beheerde threads worden uitgevoerd, kunt u zien dat er een willekeurig aantal tijdelijke garbagecollection's optreedt.

Bepalen wat een garbagecollection heeft geactiveerd

  • Voer in het WinDbg- of Visual Studio-foutopsporingsprogramma met de sos-foutopsporingsprogramma-extensie geladen de volgende opdracht in om alle threads met hun aanroepstacks weer te geven:

    ~*Kb

    Met deze opdracht wordt uitvoer weergegeven die vergelijkbaar is met de volgende.

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

    Als de garbagecollection is veroorzaakt door een melding van weinig geheugen van het besturingssysteem, is de aanroepstack vergelijkbaar, behalve dat de thread de finalizer-thread is. De finalizer-thread krijgt een asynchrone melding over weinig geheugen en veroorzaakt de garbagecollection.

    Als de garbagecollection is veroorzaakt door geheugentoewijzing, wordt de stack als volgt weergegeven:

    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
    

    Een Just-In-Time-helper (JIT_New*) belt GCHeap::GarbageCollectGenerationuiteindelijk . Als u bepaalt dat de garbagecollection van de tweede generatie wordt veroorzaakt door toewijzingen, moet u bepalen welke objecten worden verzameld door een garbagecollection van de tweede generatie en hoe u deze kunt vermijden. Dat wil gezegd, u wilt het verschil bepalen tussen het begin en het einde van een garbagecollection van de tweede generatie, en de objecten die de verzameling van de tweede generatie hebben veroorzaakt.

    Voer bijvoorbeeld de volgende opdracht in het foutopsporingsprogramma in om het begin van een verzameling van de tweede generatie weer te geven:

    !dumpheap –stat

    Voorbeelduitvoer (ingekort om de objecten weer te geven die de meeste ruimte gebruiken):

    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
    

    Herhaal de opdracht aan het einde van generatie 2:

    !dumpheap –stat

    Voorbeelduitvoer (ingekort om de objecten weer te geven die de meeste ruimte gebruiken):

    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
    

    De double[] objecten zijn verdwenen vanaf het einde van de uitvoer, wat betekent dat ze zijn verzameld. Deze objecten zijn ongeveer 70 MB. De resterende objecten zijn niet veel veranderd. Daarom waren deze double[] objecten de reden waarom deze generatie 2 garbagecollection plaatsvond. De volgende stap is om te bepalen waarom de double[] objecten er zijn en waarom ze zijn gestorven. U kunt de codeontwikkelaar vragen waar deze objecten vandaan komen of u kunt de gcroot opdracht gebruiken.

Bepalen of een hoog CPU-gebruik wordt veroorzaakt door garbagecollection

  • Correleert de prestatiemeteritemwaarde van het % Time in GC geheugen met de procestijd.

    Als de % Time in GC waarde op hetzelfde moment als procestijd piekt, veroorzaakt garbagecollection een hoog CPU-gebruik. Anders kunt u de toepassing profileren om te bepalen waar het hoge gebruik plaatsvindt.

Zie ook