Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule opisano problemy związane z odzyskiwaniem pamięci i użyciem pamięci. Rozwiązuje problemy związane z zarządzaną stertą i wyjaśnia, jak zminimalizować wpływ zbierania śmieci na aplikacje. Każdy problem zawiera linki do procedur, których można użyć do zbadania problemów.
Narzędzia do analizy wydajności
W poniższych sekcjach opisano narzędzia dostępne do badania problemów z użyciem pamięci i zbieraniem śmieci. Procedury przedstawione w dalszej części tego artykułu dotyczą tych narzędzi.
Liczniki wydajności pamięci
Liczniki wydajności umożliwiają zbieranie danych dotyczących wydajności. Aby uzyskać instrukcje, zobacz Profilowanie środowiska uruchomieniowego. Kategoria liczników wydajności pamięci .NET CLR, zgodnie z opisem w temacie Liczniki wydajności na platformie .NET, zawiera informacje na temat zbieracza pamięci.
Debugowanie za pomocą SOS
Do inspekcji obiektów na zarządzanym stercie możesz użyć debugera systemu Windows (WinDbg).
Aby zainstalować usługę WinDbg, zainstaluj narzędzia debugowania dla systemu Windows na stronie Pobieranie narzędzi debugowania dla systemu Windows .
Zdarzenia ETW odzyskiwania pamięci
Śledzenie zdarzeń dla systemu Windows (ETW) to system śledzenia, który uzupełnia obsługę profilowania i debugowania zapewnianą przez platformę .NET. Począwszy od programu .NET Framework 4, zdarzenia ETW zbierania śmieci przechwytują przydatne informacje dotyczące analizowania zarządzanej sterty pod względem statystycznym. Na przykład zdarzenie GCStart_V1
, które jest zgłaszane, gdy ma nastąpić kolekcja śmieci, zawiera następujące informacje:
- Która generacja obiektów jest kolekcjonowana.
- Co wyzwoliło odśmiecanie pamięci.
- Typ zbierania śmieci (współbieżne lub niewspółbieżne).
Rejestrowanie zdarzeń ETW jest wydajne i nie będzie maskować żadnych problemów z wydajnością związanych z odzyskiwaniem pamięci. Proces może udostępniać własne zdarzenia obok zdarzeń ETW. Po zalogowaniu zarówno zdarzenia aplikacji, jak i zdarzenia kolekcji śmieci mogą być skorelowane, aby określić, jak i kiedy występują problemy ze stertą. Na przykład aplikacja serwera może dostarczać zdarzenia na początku i na końcu żądania klienta.
Interfejs API profilowania
Interfejsy profilowania środowiska uruchomieniowego języka wspólnego (CLR) zawierają szczegółowe informacje o obiektach, które zostały zmienione podczas procesu odśmiecania pamięci. Profilera można powiadomić, kiedy rozpoczyna się i kończy zbieranie śmieci. Może dostarczać raporty o obiektach na zarządzanym stercie, w tym identyfikację obiektów w każdej generacji. Aby uzyskać więcej informacji, zobacz Profilowanie — omówienie.
Profilerzy mogą dostarczać kompleksowe informacje. Jednak złożone profileery mogą potencjalnie modyfikować zachowanie aplikacji.
Monitorowanie zasobów domeny aplikacji
Począwszy od programu .NET Framework 4, monitorowanie zasobów domeny aplikacji (ARM) umożliwia hostom monitorowanie użycia procesora i pamięci przez domenę aplikacji. Aby uzyskać więcej informacji, zobacz Application Domain Resource Monitoring (Monitorowanie zasobów domeny aplikacji).
Rozwiązywanie problemów z wydajnością
Pierwszym krokiem jest ustalenie, czy problem jest rzeczywiście wyrzucanie elementów bezużytecznych. Jeśli okaże się, że tak jest, wybierz z poniższej listy, aby rozwiązać problem.
- Zgłaszany jest wyjątek braku pamięci
- Proces używa zbyt dużej ilości pamięci
- Moduł odśmiecający śmieci nie odzyskuje obiektów wystarczająco szybko
- Zarządzana sterta jest zbyt rozdrobniona
- Przerwy w procesie odśmiecania pamięci są zbyt długie
- Generacja 0 jest zbyt duża
- Zużycie CPU podczas zbierania śmieci jest zbyt wysokie
Problem: Występuje wyjątek związany z brakiem pamięci
Istnieją dwa uzasadnione przypadki, w których można wyrzucić kontrolowany OutOfMemoryException.
Zabraknie pamięci wirtualnej.
Kolektor śmieci przydziela pamięć z systemu w segmentach o wstępnie określonym rozmiarze. Jeśli alokacja wymaga dodatkowego segmentu, ale w przestrzeni pamięci wirtualnej procesu nie ma sąsiadującego wolnego bloku, alokacja zarządzanej sterty nie powiedzie się.
Brak wystarczającej ilości pamięci fizycznej do przydzielenia.
Testy wydajności |
---|
Ustal, czy wyjątek braku pamięci jest zarządzany. Określ, ile pamięci wirtualnej można zarezerwować. Ustal, czy jest wystarczająca ilość pamięci fizycznej. |
Jeśli ustalisz, że wyjątek nie jest uzasadniony, skontaktuj się z działem obsługi klienta i wsparciem technicznym firmy Microsoft, aby uzyskać następujące informacje.
- Stos z wyjątkiem braku pamięci w zarządzanym środowisku.
- Pełny zrzut pamięci.
- Dane, które dowodzą, że nie jest to uzasadniony wyjątek braku pamięci, w tym dane pokazujące, że pamięć wirtualna lub fizyczna nie jest problemem.
Problem: Proces używa zbyt dużej ilości pamięci
Typowym założeniem jest to, że użycie pamięci wyświetlane na karcie Wydajność Menedżera zadań systemu Windows może wskazywać, kiedy jest używana zbyt duża ilość pamięci. Jednak ten ekran odnosi się do zestawu roboczego; nie udostępnia informacji o użyciu pamięci wirtualnej.
Jeśli ustalisz, że problem jest spowodowany przez zarządzaną stertę, musisz zmierzyć zarządzaną stertę w czasie, aby określić wszelkie wzorce.
Jeśli ustalisz, że problem nie jest spowodowany przez zarządzaną stertę, musisz użyć debugowania natywnego.
Problem: Moduł odśmiecający nie odzyskuje obiektów wystarczająco szybko
Gdy wygląda na to, że obiekty nie są usuwane zgodnie z oczekiwaniami podczas zbierania śmieci, należy określić, czy istnieją silne odwołania do tych obiektów.
Ten problem może również wystąpić, jeśli nie było zebrania śmieci dla generacji, która zawiera martwy obiekt, co oznacza, że finalizator martwego obiektu nie został uruchomiony. Na przykład, jest to możliwe w przypadku uruchamiania aplikacji w jednowątkowym apartamencie (STA), gdy wątek obsługujący kolejkę finalizatora nie może do niej wywołać.
Testy wydajności |
---|
Sprawdź odwołania do obiektów. Ustal, czy został uruchomiony finalizator. Ustal, czy istnieją obiekty oczekujące na sfinalizowanie. |
Problem: Zarządzany stos jest zbyt fragmentaryzowany
Poziom fragmentacji jest obliczany jako stosunek wolnego miejsca do całkowitej przydzielonej pamięci dla generacji. W przypadku generacji 2 akceptowalny poziom fragmentacji wynosi nie więcej niż 20%. Ponieważ generacja 2 może być bardzo duża, stosunek fragmentacji jest ważniejszy niż wartość bezwzględna.
Brak wolnego miejsca w generacji 0 nie jest problemem, ponieważ jest to generacja, w której są przydzielane nowe obiekty.
Fragmentacja zawsze występuje w dużej stercie obiektów, ponieważ nie jest kompresowana. Wolne sąsiadujące obiekty są naturalnie scalane w jedną przestrzeń, aby zaspokoić duże żądania alokacji obiektów.
Fragmentacja może stać się problemem w generacji 1 i 2. Jeśli te generacje mają dużą ilość wolnego miejsca po zbieraniu śmieci, użycie obiektów w aplikacji może wymagać modyfikacji, a także należy rozważyć ponowną ocenę okresu istnienia obiektów długoterminowych.
Nadmierne przypinanie obiektów może zwiększyć fragmentację. Jeśli fragmentacja jest duża, zbyt wiele obiektów mogło zostać przypiętych.
Jeśli fragmentacja pamięci wirtualnej uniemożliwia modułowi odśmiecania pamięci dodawanie segmentów, przyczyny mogą być następujące:
Częste ładowanie i rozładowywanie wielu małych zestawów.
Przechowywanie zbyt wielu odwołań do obiektów COM podczas współpracy z kodem niezarządzanym.
Tworzenie dużych obiektów przejściowych, co powoduje, że duże sterty obiektów często przydzielają i zwalniają segmenty sterty.
Podczas hostowania środowiska CLR aplikacja może zażądać, aby moduł odśmiecacz pamięci zachował swoje segmenty. Zmniejsza to częstotliwość alokacji segmentów. Jest to realizowane przy użyciu flagi STARTUP_HOARD_GC_VM w Wyliczeniu STARTUP_FLAGS.
Testy wydajności |
---|
Określ ilość wolnego miejsca na zarządzanym stercie. Określ liczbę przypiętych obiektów. |
Jeśli uważasz, że nie ma uzasadnionej przyczyny fragmentacji, skontaktuj się z działem obsługi klienta i pomocy technicznej firmy Microsoft.
Problem: Przerwy w gospodarowaniu pamięcią są zbyt długie
Zbieranie śmieci działa w czasie rzeczywistym o charakterze miękkim, więc aplikacja musi być w stanie tolerować pewne przerwy. Kryterium miękkiego czasu rzeczywistego jest to, że 95% operacji musi zakończyć się na czas.
W przypadku współbieżnego odzyskiwania pamięci zarządzane wątki mogą działać podczas zbierania, co oznacza, że pauzy są bardzo krótkie.
Tymczasowe odśmiecanie pamięci (generacje 0 i 1) trwa tylko kilka milisekund, więc skrócenie przerw zazwyczaj nie jest możliwe. Można jednak zmniejszyć liczbę wstrzymań w kolekcjach generacji 2, zmieniając wzorzec żądań alokacji przez aplikację.
Kolejną, dokładniejszą metodą jest użycie zdarzeń ETW związanych z oczyszczaniem pamięci. Chronometraż kolekcji można znaleźć, dodając różnice sygnatur czasowych dla sekwencji zdarzeń. Cała sekwencja zbierania obejmuje zawieszenie silnika wykonawczego, sam proces zbierania śmieci i wznowienie działania silnika wykonawczego.
Możesz użyć powiadomień Garbage Collection, aby określić, czy serwer zbliża się do kolekcji generacji 2 i czy przekierowanie żądań do innego serwera może złagodzić problemy z przerwami.
Testy wydajności |
---|
Określ czas trwania procesów oczyszczania pamięci. Ustal, co spowodowało czyszczenie pamięci. |
Problem: Generacja 0 jest zbyt duża
Generacja 0 może mieć większą liczbę obiektów w systemie 64-bitowym, zwłaszcza w przypadku używania odzyskiwania pamięci serwera zamiast odzyskiwania pamięci stacji roboczej. Wynika to z faktu, że próg wyzwalania zbierania śmieci generacji 0 jest wyższy w tych środowiskach wykonywania, ponadto kolekcje generacji 0 mogą być znacznie większe. Wydajność jest poprawiana, gdy aplikacja przydziela więcej pamięci przed uruchomieniem zarządzania pamięcią.
Problem: Zużycie CPU podczas kolekcji śmieci jest za wysokie
Użycie procesora będzie wysokie podczas kolekcji śmieci. Jeśli znaczna ilość czasu procesu jest poświęcana na odzyskiwanie pamięci, liczba kolekcji jest zbyt duża lub kolekcja trwa zbyt długo. Zwiększona szybkość alokacji obiektów na zarządzanym stercie powoduje częstsze odśmiecanie pamięci. Zmniejszenie szybkości alokacji zmniejsza częstotliwość czyszczenia pamięci.
Współczynniki alokacji można monitorować przy użyciu licznika Allocated Bytes/second
wydajności. Aby uzyskać więcej informacji, zobacz Liczniki wydajności na platformie .NET.
Czas trwania kolekcji jest przede wszystkim czynnikiem liczby obiektów, które przetrwają po alokacji. Kolektor śmieci musi przechodzić przez dużą ilość pamięci, jeśli wiele obiektów pozostaje do zebrania. Praca kompaktowania ocalałych jest czasochłonna. Aby określić, ile obiektów zostało obsłużonych podczas zbierania śmieci, ustaw punkt przerwania w debugerze na końcowym etapie dla określonej generacji.
Testy wydajności |
---|
Ustal, czy wysokie użycie CPU jest spowodowane przez kolektor śmieci. Ustaw punkt przerwania na końcu zbierania śmieci. |
Wskazówki dotyczące rozwiązywania problemów
W tej sekcji opisano wytyczne, które należy wziąć pod uwagę podczas rozpoczynania badań.
Odzyskiwanie pamięci stacji roboczej lub serwera
Ustal, czy używasz poprawnego typu odzyskiwania pamięci. Jeśli Twoja aplikacja używa wielu wątków i instancji obiektów, użyj kolekcji śmieci serwera zamiast kolekcji śmieci stacji roboczej. Zarządzanie pamięcią serwera jest wielowątkowe, podczas gdy zarządzanie pamięcią stacji roboczej wymaga wielu wystąpień aplikacji do uruchamiania własnych wątków zarządzania pamięcią i konkurowania o czas CPU.
Aplikacja, która ma niskie obciążenie i wykonuje zadania rzadko w tle, takie jak usługa, może używać odśmiecania pamięci na stacji roboczej z wyłączonym działaniem współbieżnym.
Kiedy należy zmierzyć rozmiar zarządzanej sterty
Jeśli nie używasz profilera, musisz ustanowić spójny wzorzec pomiaru w celu efektywnego diagnozowania problemów z wydajnością. Rozważ następujące kwestie, aby ustalić harmonogram:
- W przypadku pomiaru po przeprowadzeniu oczyszczania pamięci generacji 2 cała zarządzana sterta będzie wolna od zbędnych (martwych) obiektów.
- Jeśli dokonasz pomiaru bezpośrednio po odśmieceniu pamięci w generacji 0, obiekty z generacji 1 i 2 jeszcze nie zostaną sprzątnięte.
- Jeśli mierzysz bezpośrednio przed procesem odzyskiwania pamięci, zmierzysz jak najwięcej alokacji zanim rozpocznie się odzyskiwanie pamięci.
- Pomiar podczas zbierania śmieci jest problematyczny, ponieważ struktury danych zarządcy pamięci nie są w prawidłowym stanie do przechodzenia i mogą nie być w stanie zapewnić pełnych wyników. Jest to celowe.
- Gdy używasz zbierania pamięci stacji roboczej z równoczesnym zbieraniem pamięci, odzyskane obiekty nie są zagęszczane, więc rozmiar sterty może być taki sam lub większy (fragmentacja może sprawiać, że wydaje się większa).
- Współbieżne odzyskiwanie pamięci w generacji 2 jest opóźnione, gdy obciążenie pamięci fizycznej jest zbyt wysokie.
W poniższej procedurze opisano sposób ustawiania punktu przerwania, aby można było zmierzyć zarządzaną stertę.
Aby ustawić punkt przerwania na końcu zbierania śmieci
W systemie WinDbg z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"
Ustaw
GcCondemnedGeneration
wartość żądanej generacji. To polecenie wymaga symboli prywatnych.To polecenie wymusza przerwanie, jeśli
RestartEE
jest wykonywane po odzyskaniu obiektów generacji 2 dla zbierania śmieci.W przypadku serwerowego zbierania śmieci tylko jeden wątek wywołuje
RestartEE
, więc punkt przerwania wystąpi tylko raz podczas zbierania śmieci generacji 2.
Procedury sprawdzania wydajności
W tej sekcji opisano następujące procedury izolowania przyczyny problemu z wydajnością:
- Ustal, czy problem jest spowodowany przez odzyskiwanie pamięci.
- Ustal, czy wyjątek braku pamięci jest zarządzany.
- Określ, ile pamięci wirtualnej można zarezerwować.
- Ustal, czy jest wystarczająca ilość pamięci fizycznej.
- Określ ilość pamięci zatwierdzanej przez zarządzaną stertę.
- Określ, ile pamięci rezerwuje zarządzana sterta.
- Zidentyfikuj duże obiekty w pokoleniu 2.
- Wyznacz referencje do obiektów.
- Ustal, czy został uruchomiony finalizator.
- Ustal, czy istnieją obiekty oczekujące na sfinalizowanie.
- Określ ilość wolnego miejsca na zarządzanym stercie.
- Określ liczbę przypiętych obiektów.
- Określ czas trwania procesów oczyszczania pamięci.
- Określ, co wyzwoliło czyszczenie pamięci.
- Ustal, czy wysokie użycie procesora CPU jest spowodowane przez zbieranie śmieci.
Aby ustalić, czy problem jest spowodowany przez proces odzyskiwania pamięci
Sprawdź następujące dwa liczniki wydajności pamięci:
% czas w GC. Przedstawia procent czasu, jaki upłynął na wykonanie odzyskiwania pamięci po ostatnim cyklu zbierania nieużytków. Użyj tego licznika, aby określić, czy kolektor śmieci poświęca zbyt dużo czasu na udostępnienie zarządzanej pamięci sterty. Jeśli czas spędzony na zbieraniu śmieci jest stosunkowo krótki, może to wskazywać na problem z zasobami poza zarządzaną stertą. Licznik ten może być niedokładny w przypadku współbieżnego lub zbierania śmieci w tle.
# Łączna liczba zatwierdzonych bajtów. Przedstawia ilość pamięci wirtualnej, która jest obecnie zatwierdzana przez moduł odśmiecający pamięć. Użyj tego licznika, aby określić, czy pamięć zużywana przez garbage collector jest nadmierną częścią pamięci używanej przez aplikację.
Większość liczników wydajności pamięci jest aktualizowana po zakończeniu każdego zbierania śmieci. W związku z tym mogą one nie odzwierciedlać bieżących warunków, o których chcesz uzyskać informacje.
Aby określić, czy wyjątek braku pamięci jest obsługiwany
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie: drukuj wyjątek (
pe
).!pe
Jeśli wyjątek jest zarządzany, OutOfMemoryException jest wyświetlany jako typ wyjątku, jak pokazano w poniższym przykładzie.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):
Jeśli dane wyjściowe nie określają wyjątku, musisz ustalić, z którego wątku pochodzi wyjątek braku pamięci. Wprowadź następujące polecenie w debugerze, aby wyświetlić wszystkie wątki ze swoimi stosami wywołań:
~\*kb
Wątek ze stosem zawierającym wywołania wyjątków jest wskazany za pomocą argumentu
RaiseTheException
. Jest to obiekt wyjątku zarządzanego.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
Aby zrzucić zagnieżdżone wyjątki, można użyć następującego polecenia.
!pe -nested
Jeśli nie znajdziesz żadnych wyjątków, wyjątek braku pamięci pochodzi z niezarządzanego kodu.
Aby określić, ile pamięci wirtualnej można zarezerwować
W systemie WinDbg z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie, aby uzyskać największy bezpłatny region:
!address -summary
Największy wolny region jest wyświetlany, jak pokazano w poniższym wyniku.
Largest free region: Base 54000000 - Size 0003A980
W tym przykładzie rozmiar największego wolnego regionu wynosi około 24000 KB (3A980 w systemie szesnastkowym). Ten obszar jest znacznie mniejszy niż to, czego potrzebuje garbage collector dla segmentu.
-lub-
Użyj polecenia
vmstat
:!vmstat
Największa wolna przestrzeń jest największą wartością w kolumnie MAXIMUM, jak pokazano w poniższych wyjściowych danych.
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
Aby ustalić, czy jest wystarczająca ilość pamięci fizycznej
Uruchom Menedżera zadań systemu Windows.
Na karcie
Performance
spójrz na zarezerwowaną wartość. (W systemie Windows 7 spójrz naCommit (KB)
wSystem group
.)Jeśli wartość
Total
znajduje się bliskoLimit
, brakuje pamięci fizycznej.
Aby określić ilość pamięci zatwierdzanej przez zarządzaną stertę
Użyj licznika
# Total committed bytes
wydajności pamięci, aby uzyskać liczbę bajtów, które są zatwierdzane przez stertę zarządzaną. Automatyczny zarządca pamięci alokuje kawałki w segmencie zgodnie z potrzebami, a nie wszystkim na raz.Uwaga / Notatka
Nie używaj
# Bytes in all Heaps
jako miernika wydajności, ponieważ nie odzwierciedla rzeczywistego wykorzystania pamięci przez stertę zarządzaną. Rozmiar generacji jest uwzględniany w tej wartości i stanowi jej rozmiar progowy, to znaczy rozmiar, który powoduje zbieranie śmieci, jeśli generacja jest wypełniona obiektami. W związku z tym ta wartość zwykle wynosi zero.
Aby określić, ile pamięci jest rezerwowane przez zarządzaną stertę
Użyj licznika
# Total reserved bytes
wydajności pamięci.Moduł odśmiecania pamięci rezerwuje pamięć w segmentach i można określić, gdzie zaczyna się segment, używając polecenia
eeheap
.Ważne
Chociaż można określić ilość pamięci przydzielanej przez mechanizm zbierania śmieci dla każdego segmentu, rozmiar segmentu jest zależny od implementacji i może ulec zmianie w okresowych aktualizacjach, w dowolnym momencie. Aplikacja nigdy nie powinna zakładać ani nie zależeć od określonego rozmiaru segmentu, ani nie powinna podejmować próby skonfigurowania ilości pamięci dostępnej dla alokacji segmentów.
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
!eeheap -gc
Wynik jest następujący.
Number of GC Heaps: 2 ------------------------------ Heap 0 (002db550) generation 0 starts at 0x02abe29c generation 1 starts at 0x02abdd08 generation 2 starts at 0x02ab0038 ephemeral segment allocation context: none segment begin allocated size 02ab0000 02ab0038 02aceff4 0x0001efbc(126908) Large object heap starts at 0x0aab0038 segment begin allocated size 0aab0000 0aab0038 0aab2278 0x00002240(8768) Heap Size 0x211fc(135676) ------------------------------ Heap 1 (002dc958) generation 0 starts at 0x06ab1bd8 generation 1 starts at 0x06ab1bcc generation 2 starts at 0x06ab0038 ephemeral segment allocation context: none segment begin allocated size 06ab0000 06ab0038 06ab3be4 0x00003bac(15276) Large object heap starts at 0x0cab0038 segment begin allocated size 0cab0000 0cab0038 0cab0048 0x00000010(16) Heap Size 0x3bbc(15292) ------------------------------ GC Heap Size 0x24db8(150968)
Adresy wskazywane przez "segment" to adresy początkowe segmentów.
Aby określić duże obiekty w generacji 2
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
!dumpheap –stat
Jeśli zarządzana sterta jest duża, może upłynąć trochę czasu, zanim
dumpheap
zakończy działanie.Możesz rozpocząć analizowanie z kilku ostatnich wierszy danych wyjściowych, ponieważ wyświetlają one listę obiektów, które używają największej ilości miejsca. Przykład:
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
Ostatni obiekt wymieniony jest ciągiem i zajmuje najwięcej miejsca. Możesz zbadać aplikację, aby zobaczyć, jak można zoptymalizować obiekty ciągów. Aby wyświetlić ciągi z zakresu od 150 do 200 bajtów, wprowadź następujące wartości:
!dumpheap -type System.String -min 150 -max 200
Przykład wyników wygląda następująco.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …
Użycie liczby całkowitej zamiast ciągu dla identyfikatora może być bardziej wydajne. Jeśli ten sam ciąg jest powtarzany tysiące razy, rozważ internowanie ciągu. Aby uzyskać więcej informacji na temat internowania ciągów, zobacz temat referencyjny dla metody String.Intern.
Aby określić odwołania do obiektów
W systemie WinDbg z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie, aby wyświetlić listę odwołań do obiektów:
!gcroot
-lub-
Aby określić odwołania do określonego obiektu, dołącz adres:
!gcroot 1c37b2ac
Korzenie znalezione na stosach mogą być fałszywie dodatnie. Aby uzyskać więcej informacji, użyj polecenia
!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
Ukończenie
gcroot
polecenia może zająć dużo czasu. Każdy obiekt, który nie jest odzyskiwany przez zbieranie śmieci, jest żywym obiektem. Oznacza to, że jakiś element główny trzyma obiekt bezpośrednio lub pośrednio, więcgcroot
ma zwrócić informacje o ścieżce do obiektu. Należy zbadać zwrócone grafy i sprawdzić, dlaczego te obiekty są nadal przywoływane.
Aby ustalić, czy został uruchomiony finalizator
Uruchom program testowy zawierający następujący kod:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Jeśli test rozwiąże ten problem, oznacza to, że moduł odśmiecania pamięci nie odzyskuje obiektów, ponieważ finalizatory dla tych obiektów zostały zawieszone. Metoda GC.WaitForPendingFinalizers umożliwia finalizatorom wykonywanie zadań i rozwiązywanie problemu.
Aby ustalić, czy istnieją obiekty oczekujące na sfinalizowanie
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
!finalizequeue
Przyjrzyj się liczbie obiektów gotowych do finalizacji. Jeśli liczba jest wysoka, musisz sprawdzić, dlaczego te finalizatory nie mogą w ogóle postępować lub nie mogą postępować wystarczająco szybko.
Aby uzyskać dane wyjściowe wątków, wprowadź następujące polecenie:
!threads -special
To polecenie udostępnia dane wyjściowe, takie jak następujące.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEE
Wątek finalizatora wskazuje, który finalizator, jeśli istnieje, jest obecnie uruchamiany. Gdy wątek finalizatora nie uruchamia żadnych finalizatorów, czeka na zdarzenie, które nakaże mu rozpocząć pracę. Przez większość czasu zauważysz, że wątek finalizatora znajduje się w tym stanie, ponieważ działa z priorytetem THREAD_HIGHEST_PRIORITY i ma bardzo szybko zakończyć wykonywanie finalizatorów, jeśli takie istnieją.
Aby ustalić ilość wolnego miejsca w zarządzanej stercie
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
!dumpheap -type Free -stat
To polecenie wyświetla całkowity rozmiar wszystkich bezpłatnych obiektów na zarządzanym stercie, jak pokazano w poniższym przykładzie.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objects
Aby określić wolne miejsce w generacji 0, wprowadź następujące polecenie dla informacji o zużyciu pamięci według generacji:
!eeheap -gc
To polecenie wyświetla dane wyjściowe podobne do poniższych. Ostatnia linia pokazuje segment przejściowy.
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)
Oblicz miejsce używane przez generację 0:
? 49e05d04-0x49521f8c
Wynik jest następujący. Generacja 0 wynosi około 9 MB.
Evaluate expression: 9321848 = 008e3d78
Następujące polecenie zrzutuje wolne miejsce w zakresie generacji 0:
!dumpheap -type Free -stat 0x49521f8c 49e05d04
Wynik jest następujący.
------------------------------ 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
Te dane wyjściowe pokazują, że segment generacji 0 na stercie używa 9 MB miejsca dla obiektów i ma 7 MB wolne. Ta analiza pokazuje, w jakim stopniu generacja 0 przyczynia się do fragmentacji. Ta ilość użycia pamięci sterty powinna być odjęta od całkowitej ilości jako przyczyna fragmentacji spowodowanej przez obiekty długoterminowe.
Aby określić liczbę przypiętych obiektów
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie:
!gchandles
Wyświetlane statystyki obejmują liczbę przypiętych uchwytów, jak pokazano w poniższym przykładzie.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Aby określić długość czasu trwania procesu zbierania śmieci
% Time in GC
Sprawdź licznik wydajności pamięci.Wartość jest obliczana przy użyciu czasu interwału próbki. Ponieważ liczniki są aktualizowane na końcu każdego odzyskiwania pamięci, bieżąca próbka będzie miała taką samą wartość jak poprzednia próbka, jeśli w interwale nie wystąpiły żadne kolekcje.
Czas kolekcji jest uzyskiwany przez pomnożenie czasu interwału próbki z wartością procentową.
W poniższych danych przedstawiono cztery interwały próbkowania w ciągu dwóch sekund w 8-sekundowym badaniu. Kolumny
Gen0
,Gen1
iGen2
pokazują całkowitą liczbę kolekcji odpadów zakończonych na koniec interwału dla tej generacji.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
Te informacje nie pokazują, kiedy miało miejsce zbieranie śmieci, ale można określić liczbę zbierania śmieci, które miały miejsce w określonym przedziale czasu. Zakładając najgorszy przypadek, dziesiąta generacja 0 zbierania śmieci zakończyła się na początku drugiego przedziału czasowego, a jedenasta generacja 0 zbierania śmieci zakończyła się na końcu trzeciego przedziału czasowego. Czas między końcem dziesiątej a końcem jedenastej kolekcji śmieci wynosi około 2 sekundy, a licznik wydajności pokazuje 3%, więc czas trwania jedenastej kolekcji śmieci generacji 0 wyniósł 60 ms (2 sekundy * 3%).
W następnym przykładzie istnieje pięć interwałów.
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
Odzyskiwanie pamięci drugiej generacji 2 rozpoczęło się w czwartym interwale i zakończyło się w piątym interwale. Zakładając najgorszy scenariusz, ostatnie zbieranie śmieci dotyczyło kolekcji generacji 0, która zakończyła się na początku trzeciego interwału, a zbieranie śmieci generacji 2 zakończyło się na końcu piątego interwału. Zatem czas między końcem zbierania śmieci generacji 0 a końcem zbierania śmieci generacji 2 wynosi 4 sekundy. Ponieważ licznik
% Time in GC
wynosi 20%, maksymalny czas, który mogło zająć odzyskiwanie pamięci generacji 2, wynosi (4 sekundy * 20% = 800 ms).Alternatywnie można określić długość odzyskiwania pamięci przy użyciu zdarzeń ETW odzyskiwania pamięci i analizować informacje, aby określić czas trwania odzyskiwania pamięci.
Na przykład następujące dane przedstawiają sekwencję zdarzeń, która wystąpiła podczas niekonkurencyjnego odzyskiwania pamięci.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEnd
Wstrzymanie zarządzanego wątku trwało 26 mikrosekund (
GCSuspendEEEnd
–GCSuspendEEBegin_V1
).Rzeczywista kolekcja śmieci trwała 4,8 ms (
GCEnd_V1
–GCStart_V1
).Wznawianie zarządzanych wątków trwało 21 μs (
GCRestartEEEnd
–GCRestartEEBegin
).Poniższy wynik przedstawia przykład zbierania odpadów w tle, obejmując pola: procesu, wątku i zdarzenia. (Nie wszystkie dane są wyświetlane).
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
Zdarzenie
GCStart_V1
w 42504816 wskazuje, że jest to odzyskiwanie pamięci w tle, ponieważ ostatnie pole to1
. Staje się to zbiórką odpadów numer 102019.Zdarzenie
GCStart
występuje, ponieważ istnieje potrzeba efemerycznego odzyskiwania pamięci przed rozpoczęciem odzyskiwania pamięci w tle. To staje się zbiórką odpadów nr 102020.Przy 42514170, zakończono kolekcję odpadów nr 102020. W tym momencie zarządzane wątki są uruchamiane ponownie. Zostało to ukończone w wątku 4372, który zainicjował odzyskiwanie pamięci w tle.
W wątku 4744 następuje zawieszenie. Jest to jedyny moment, w którym odzyskiwanie pamięci w tle musi zawiesić zarządzane wątki. Ten czas trwania wynosi około 99 ms ((63784407-63685394)/1000).
Zdarzenie
GCEnd
odzyskiwania pamięci w tle odbędzie się na 89931423. Oznacza to, że odzyskiwanie pamięci w tle trwało około 47 sekund ((89931423-42504816)/1000).Podczas działania zarządzanych wątków można zobaczyć dowolną liczbę efemerycznych procesów zbierania śmieci.
Aby określić, co wyzwoliło odzyskiwanie pamięci
W debugerze WinDbg lub Visual Studio z załadowanym rozszerzeniem debugera SOS wprowadź następujące polecenie, aby wyświetlić wszystkie wątki ze swoimi stosami wywołań:
~*Baza wiedzy
To polecenie wyświetla dane wyjściowe podobne do poniższych.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
Jeśli odzyskiwanie pamięci zostało wywołane przez powiadomienie o niskiej ilości pamięci z systemu operacyjnego, stos wywołań jest podobny, z tą różnicą, że wątek to wątek finalizujący. Wątek finalizatora otrzymuje asynchroniczne powiadomienie o niskiej ilości dostępnej pamięci i inicjuje odśmiecanie pamięci.
Jeśli odśmiecanie pamięci zostało spowodowane przez alokację pamięci, stos wygląda następująco:
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
Pomocnik just-in-time (
JIT_New*
) w końcu wywołujeGCHeap::GarbageCollectGeneration
. Jeśli ustalisz, że odśmiecanie pamięci generacji 2 jest spowodowane przez alokacje, musisz ustalić, które obiekty są zbierane podczas odśmiecania pamięci generacji 2 i jak tego unikać. Oznacza to, że chcesz określić różnicę między rozpoczęciem a końcem odzyskiwania pamięci generacji 2 oraz obiektami, które spowodowały zbieranie pamięci generacji 2.Na przykład wprowadź następujące polecenie w debugerze, aby wyświetlić początek kolekcji generacji 2:
!dumpheap –stat
Przykładowe dane wyjściowe (skrócone, aby pokazać obiekty, które używają największej przestrzeni):
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
Powtórz polecenie na końcu generacji 2:
!dumpheap –stat
Przykładowe dane wyjściowe (skrócone, aby pokazać obiekty, które używają największej przestrzeni):
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
Obiekty
double[]
zniknęły z końca danych wyjściowych, co oznacza, że zostały zebrane. Te obiekty stanowią około 70 MB. Pozostałe obiekty nie zmieniły się zbytnio. W związku z tym tedouble[]
obiekty były powodem, dla którego doszło do odśmiecania pamięci tej generacji 2. Następnym krokiem jest ustalenie, dlaczego obiektydouble[]
są tam i dlaczego przestały działać. Możesz zapytać dewelopera kodu, z którego pochodzą te obiekty, lub użyćgcroot
polecenia .
Aby ustalić, czy wysokie użycie CPU wynika z oczyszczania pamięci
Skoreluj wartość licznika
% Time in GC
wydajności pamięci z czasem procesu.% Time in GC
Jeśli wartość gwałtownie wzrasta w tym samym czasie co czas trwania procesu, odzyskiwanie pamięci powoduje wysokie użycie procesora. W przeciwnym razie zaprofiluj aplikację, aby znaleźć miejsce występowania wysokiego użycia.