Ogólna rada jest taka, że nie powinieneś wywoływać GC.Collect
z kodu, ale jakie są wyjątki od tej reguły?
Przychodzi mi na myśl tylko kilka bardzo konkretnych przypadków, w których wymuszenie zbierania elementów bezużytecznych może mieć sens.
Przykładem, który przychodzi na myśl, jest służba, która budzi się co jakiś czas, wykonuje jakieś zadanie, a potem długo śpi. W takim przypadku dobrym pomysłem może być wymuszenie zbierania danych, aby zapobiec zatrzymywaniu większej ilości pamięci niż potrzeba przez proces, który wkrótce ma być bezczynny.
Czy są jakieś inne przypadki, w których można zadzwonić GC.Collect
?
c#
.net
garbage-collection
Brian Rasmussen
źródło
źródło
Odpowiedzi:
Jeśli masz dobry powód, by sądzić, że znaczący zestaw obiektów - szczególnie te, co do których podejrzewasz, że należą do generacji 1 i 2 - kwalifikuje się teraz do zbierania elementów bezużytecznych i że teraz byłby odpowiedni czas na zebranie pod względem niewielkiego spadku wydajności .
Dobrym przykładem jest sytuacja, gdy właśnie zamknąłeś duży formularz. Wiesz, że wszystkie kontrolki interfejsu użytkownika można teraz zbierać jako śmieci, a bardzo krótka przerwa podczas zamykania formularza prawdopodobnie nie będzie zauważalna dla użytkownika.
AKTUALIZACJA 2.7.2018
Od .NET 4.5 - jest
GCLatencyMode.LowLatency
iGCLatencyMode.SustainedLowLatency
. Podczas wchodzenia i wychodzenia z któregokolwiek z tych trybów zaleca się wymuszenie pełnego GC za pomocąGC.Collect(2, GCCollectionMode.Forced)
.Od .NET 4.6 - istnieje
GC.TryStartNoGCRegion
metoda (używana do ustawiania wartości tylko do odczytuGCLatencyMode.NoGCRegion
). To może samo w sobie wykonać pełne blokowanie wyrzucania elementów bezużytecznych w celu zwolnienia wystarczającej ilości pamięci, ale biorąc pod uwagę, że przez pewien czas nie zezwalamy na GC, uważam, że dobrym pomysłem jest również wykonanie pełnego GC przed i po.Źródło: inżynier firmy Microsoft Ben Watson's: Writing High-Performance .NET Code , 2nd Ed. 2018.
Widzieć:
źródło
GC.Collect
, w zasadzie mówisz odśmiecaczowi, że znasz lepiej niż to dla odmiany. Dlaczego nie podoba ci się przykład?GC.Collect()
zażąda, aby GC wykonał pełną kolekcję. Jeśli wiesz, że właśnie utworzyłeś wiele wcześniej długowiecznych obiektów kwalifikujących się do czyszczenia pamięci i uważasz, że użytkownik rzadziej zauważy teraz krótką przerwę niż później, wydaje się całkowicie rozsądne myślenie, że teraz jest lepiej czas, aby zachęcić do zebrania, niż pozwolić, aby stało się to później.Używam
GC.Collect
tylko podczas pisania surowych zestawów testowych wydajności / profilera; czyli mam dwa (lub więcej) bloki kodu do przetestowania - coś takiego:Tak więc
TestA()
iTestB()
biegnij z możliwie podobnym stanem - tj.TestB()
Nie zostaje uderzony tylko dlatego, żeTestA
zostawił go bardzo blisko punktu krytycznego.Klasycznym przykładem może być prosty plik exe konsoli (
Main
metoda sortowania - na przykład wystarczająca do opublikowania tutaj), który pokazuje różnicę między zapętloną konkatenacją ciągów aStringBuilder
.Gdybym potrzebował czegoś precyzyjnego, byłyby to dwa całkowicie niezależne testy - ale często to wystarczy, jeśli chcemy po prostu zminimalizować (lub znormalizować) GC podczas testów, aby uzyskać przybliżone odczucie zachowania.
Podczas kodu produkcyjnego? Jeszcze go nie używałem ;-p
źródło
W większości przypadków najlepszym rozwiązaniem jest niewymuszanie wyrzucania elementów bezużytecznych. (Każdy system, nad którym pracowałem, wymuszał zbieranie elementów bezużytecznych, miał podkreślane problemy, których rozwiązanie wyeliminowałoby potrzebę wymuszania zbierania elementów bezużytecznych i znacznie przyspieszyło system.)
Jest kilka przypadków , w których wiesz więcej na temat użycia pamięci niż garbage collector. Jest to mało prawdopodobne w przypadku aplikacji dla wielu użytkowników lub usługi, która odpowiada na więcej niż jedno żądanie naraz.
Jednak w niektórych procesach wsadowych wiesz więcej niż GC. Np. Rozważ taką aplikację.
Państwo może być w stanie dokonać przypadek (po ostrożnym) badania, które powinny wymusić pełną kolekcję śmieci po trzeba przetworzyć każdy plik.
Innym przypadkiem jest usługa, która budzi się co kilka minut w celu przetworzenia niektórych elementów i nie utrzymuje żadnego stanu podczas snu . Następnie zmuszając pełną kolekcję tuż przed pójściem spać może się opłacać.
Wolałbym mieć API do zbierania śmieci, kiedy mógłbym dać mu wskazówki na temat tego typu rzeczy bez konieczności wymuszania GC samodzielnie.
Zobacz też „ Ciekawostki o występach Rico Marianiego ”
źródło
Jeden przypadek ma miejsce, gdy próbujesz przetestować kod jednostkowy, który używa WeakReference .
źródło
W dużych systemach 24/7 lub 24/6 - systemach, które reagują na wiadomości, żądania RPC lub w sposób ciągły sondują bazę danych lub proces - warto mieć sposób na identyfikację wycieków pamięci. W tym celu staram się dodawać do aplikacji mechanizm tymczasowego wstrzymania przetwarzania, a następnie pełnego usuwania pamięci. Wprowadza to system w stan spoczynku, w którym pozostała pamięć jest albo legalnie długowieczną pamięcią (pamięci podręczne, konfiguracja itp.), Albo „wyciekła” (obiekty, które nie są oczekiwane lub pożądane do zrootowania, ale w rzeczywistości są).
Posiadanie tego mechanizmu znacznie ułatwia profilowanie użycia pamięci, ponieważ raporty nie będą zaćmione szumem z aktywnego przetwarzania.
Aby mieć pewność, że zbierzesz wszystkie śmieci, musisz wykonać dwie kolekcje:
Jako pierwsza kolekcja spowoduje, że wszystkie obiekty z finalizatorami zostaną sfinalizowane (ale w rzeczywistości nie będą usuwać tych obiektów do śmieci). Drugi GC odrzuci te sfinalizowane obiekty.
źródło
Możesz wywołać GC.Collect (), gdy wiesz coś o naturze aplikacji, której nie ma garbage collector. Kuszące jest myślenie, że jako autorka jest to bardzo prawdopodobne. Jednak prawda jest taka, że GC to całkiem dobrze napisany i przetestowany system ekspercki i rzadko można wiedzieć coś o ścieżkach kodu niskiego poziomu, których nie ma.
Najlepszym przykładem miejsca, w którym możesz mieć dodatkowe informacje, jest aplikacja, która przełącza się między okresami bezczynności i okresami dużego obciążenia. Chcesz mieć możliwie najlepszą wydajność w okresach wzmożonego ruchu i dlatego chcesz wykorzystać czas bezczynności na sprzątanie.
Jednak w większości przypadków GC jest wystarczająco sprytny, aby to zrobić.
źródło
Jako rozwiązanie fragmentacji pamięci. Wydostawałem się z wyjątków pamięci podczas zapisywania dużej ilości danych w strumieniu pamięci (odczyt ze strumienia sieciowego). Dane zostały zapisane w fragmentach 8K. Po osiągnięciu 128M nastąpił wyjątek, mimo że było dużo dostępnej pamięci (ale była pofragmentowana). Wywołanie GC.Collect () rozwiązało problem. Po naprawie mogłem obsłużyć ponad 1G.
źródło
Spójrz na ten artykuł autorstwa Rico Mariani. Podaje dwie zasady, kiedy należy wywołać GC.Collect (zasada 1 to: „Nie”):
Kiedy dzwonić do GC.Collect ()
źródło
Jednym z przypadków, w których wywołanie GC.Collect () jest prawie konieczne, jest automatyzacja pakietu Microsoft Office za pośrednictwem Interop. Obiekty COM dla pakietu Office nie lubią automatycznie zwalniać i mogą powodować, że wystąpienia produktu Office zajmują bardzo duże ilości pamięci. Nie jestem pewien, czy jest to problem, czy z założenia. W Internecie jest wiele postów na ten temat, więc nie będę wchodził w zbyt wiele szczegółów.
Podczas programowania przy użyciu Interop każdy obiekt COM powinien zostać zwolniony ręcznie, zwykle przy użyciu metody Marshal.ReleseComObject (). Dodatkowo, ręczne wywołanie Garbage Collection może trochę pomóc w „uporządkowaniu”. Wywołanie następującego kodu, gdy skończysz z obiektami Interop, wydaje się całkiem pomóc:
Z mojego osobistego doświadczenia wynika, że użycie kombinacji ReleaseComObject i ręcznego wywoływania funkcji czyszczenia pamięci znacznie zmniejsza użycie pamięci przez produkty Office, w szczególności Excel.
źródło
Robiłem testy wydajności na tablicy i liście:
i dostałem
OutOfMemoryException
w metodzie GetSomeNumbers_Enumerable_Range jedynym obejściem jest zwolnienie pamięci przez:źródło
W twoim przykładzie myślę, że wywołanie GC.Collect nie jest problemem, ale raczej problemem jest projekt.
Jeśli masz zamiar budzić się w określonych odstępach czasu (ustalonych godzinach), wtedy twój program powinien być przygotowany do pojedynczego wykonania (wykonać zadanie raz), a następnie zakończyć. Następnie należy ustawić program jako zaplanowane zadanie do uruchamiania w zaplanowanych odstępach czasu.
W ten sposób nie musisz martwić się o wywoływanie GC.Collect (co rzadko, jeśli w ogóle, powinieneś robić).
Mając to na uwadze, Rico Mariani opublikował świetny wpis na blogu na ten temat, który można znaleźć tutaj:
http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx
źródło
Jednym z przydatnych miejsc do wywołania GC.Collect () jest test jednostkowy, gdy chcesz sprawdzić, czy nie tworzysz przecieku pamięci (np. Jeśli robisz coś z WeakReferences lub ConditionalWeakTable, dynamicznie generowany kod itp.).
Na przykład mam kilka testów takich jak:
Można argumentować, że używanie WeakReferences jest problemem samo w sobie, ale wydaje się, że jeśli tworzysz system, który opiera się na takim zachowaniu, wywołanie GC.Collect () jest dobrym sposobem na weryfikację takiego kodu.
źródło
Wpis w blogu Scotta Holdena na temat tego, kiedy (a kiedy nie) wywoływać GC.Collect jest specyficzny dla .NET Compact Framework , ale reguły mają zastosowanie do wszystkich zarządzanych programów.
źródło
Krótka odpowiedź brzmi: nigdy!
źródło
źródło
Są sytuacje, w których lepiej jest bezpiecznie niż żałować.
Oto jedna sytuacja.
Możliwe jest utworzenie niezarządzanej biblioteki DLL w języku C # przy użyciu przepisywania IL (ponieważ są sytuacje, w których jest to konieczne).
Załóżmy na przykład, że biblioteka DLL tworzy tablicę bajtów na poziomie klasy - ponieważ wiele wyeksportowanych funkcji wymaga do nich dostępu. Co się stanie, gdy biblioteka DLL zostanie zwolniona? Czy w tym momencie moduł odśmiecania pamięci jest automatycznie wywoływany? Nie wiem, ale będąc niezarządzaną biblioteką DLL, jest całkowicie możliwe, że GC nie jest wywoływana. Byłby to duży problem, gdyby nie został wywołany. Gdy biblioteka DLL zostanie wyładowana, tak samo będzie z programem odśmiecającym - więc kto będzie odpowiedzialny za zbieranie ewentualnych śmieci i jak to zrobi? Lepiej jest zastosować moduł odśmiecania pamięci C #. Mieć funkcję czyszczenia (dostępną dla klienta DLL), w której zmienne poziomu klasy są ustawione na null i wywoływany jest moduł odśmiecania pamięci.
Lepiej dmuchać na zimne.
źródło
nadal nie jestem tego pewien. Pracuję od 7 lat na serwerze aplikacji. Nasze większe instalacje korzystają z 24 GB pamięci RAM. Jest bardzo wielowątkowy, a WSZYSTKIE wywołania GC.Collect () napotkały naprawdę straszne problemy z wydajnością.
Wiele komponentów innych firm korzystało z GC.Collect (), gdy uważali, że jest to sprytne, aby zrobić to teraz. Tak więc prosta grupa raportów programu Excel blokowała serwer aplikacji dla wszystkich wątków kilka razy na minutę.
Musieliśmy refaktoryzować wszystkie składniki innych firm, aby usunąć wywołania GC.Collect (), i po wykonaniu tej czynności wszystko działało dobrze.
Ale używam serwerów również na Win32 i tutaj zacząłem intensywnie używać GC.Collect () po otrzymaniu OutOfMemoryException.
Ale nie jestem też tego pewien, ponieważ często zauważyłem, że kiedy dostaję OOM na 32-bitowym, i ponownie próbuję uruchomić tę samą operację, bez wywoływania GC.Collect (), po prostu działało dobrze.
Jedną rzeczą, nad którą się zastanawiam, jest sam wyjątek OOM ... Gdybym napisał .Net Framework i nie mogę przydzielić bloku pamięci, użyłbym GC.Collect (), defragmentacji pamięci (??), spróbuj ponownie , a jeśli nadal nie mogę znaleźć wolnego bloku pamięci, wyrzuciłbym wyjątek OOM.
Lub przynajmniej uczyń to zachowanie jako konfigurowalną opcją, ze względu na wady problemu z wydajnością w GC.Collect.
Teraz w mojej aplikacji jest mnóstwo kodu, który „rozwiązuje” problem:
(Zwróć uwagę, że zachowanie Thread.Sleep () jest w rzeczywistości zachowaniem specyficznym dla aplikacji, ponieważ uruchamiamy usługę buforowania ORM, a usługa zajmuje trochę czasu, aby zwolnić wszystkie buforowane obiekty, jeśli pamięć RAM przekracza niektóre wstępnie zdefiniowane wartości. kilka sekund za pierwszym razem i wydłużał czas oczekiwania przy każdym wystąpieniu OOM.)
źródło
GC.Collect
. Ponieważ ma to wpływ na całą aplikację, tylko aplikacja powinna to robić (jeśli w ogóle).If i would have written the .Net Framework, and i can't alloc a memory block, i would use GC.Collect(),
- Myślę, że już to robią - widziałem oznaki, że jednym z wewnętrznych wyzwalaczy GC są pewne błędy w alokacji pamięci.Powinieneś unikać używania GC.Collect (), ponieważ jest bardzo drogi. Oto przykład:
WYNIK TESTU: WYKORZYSTANIE PROCESORA 12%
Kiedy zmienisz na to:
WYNIK TESTU: ZUŻYCIE PROCESORA 2-3%
źródło
Innym powodem jest otwarcie portu szeregowego na porcie USB COM, a następnie odłączenie urządzenia USB. Ponieważ port SerialPort został otwarty, zasób zawiera odniesienie do wcześniej podłączonego portu w rejestrze systemu. Rejestr systemu będzie wtedy zawierał nieaktualne dane , więc lista dostępnych portów będzie błędna. Dlatego port musi być zamknięty.
Wywołanie SerialPort.Close () na porcie wywołuje Dispose () na obiekcie, ale pozostaje on w pamięci do czasu faktycznego uruchomienia funkcji czyszczenia pamięci, co powoduje, że rejestr pozostaje nieaktualny, dopóki moduł odśmiecania pamięci nie zdecyduje się zwolnić zasobu.
Z https://stackoverflow.com/a/58810699/8685342 :
źródło
To nie jest tak istotne dla pytania, ale w przypadku przekształceń XSLT w .NET (XSLCompiledTranform) możesz nie mieć wyboru. Innym kandydatem jest formant MSHTML.
źródło
Jeśli używasz wersji .net mniejszej niż 4.5, ręczne zbieranie danych może być nieuniknione (szczególnie jeśli masz do czynienia z wieloma „dużymi obiektami”).
ten link opisuje, dlaczego:
https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/
źródło
Jednym z dobrych powodów wywoływania GC są małe komputery ARM z małą ilością pamięci, takie jak Raspberry PI (działające z monofonicznym). Jeśli nieprzydzielone fragmenty pamięci zajmują zbyt dużo systemowej pamięci RAM, system operacyjny Linux może stać się niestabilny. Mam aplikację, w której muszę co sekundę wywoływać GC (!), Aby pozbyć się problemów z przepełnieniem pamięci.
Innym dobrym rozwiązaniem jest wyrzucanie przedmiotów, gdy nie są już potrzebne. Niestety w wielu przypadkach nie jest to takie proste.
źródło
Ponieważ istnieje sterta małych obiektów (SOH) i sterta dużych obiektów (LOH)
Możemy wywołać GC.Collect (), aby wyczyścić obiekt usuwania referencji w SOP i przenieść żyjący obiekt do następnej generacji.
W .net4.5 możemy również kompaktować LOH przy użyciu trybu largeobjectheapcompactionmode
źródło
Jeśli tworzysz wiele nowych
System.Drawing.Bitmap
obiektów, Garbage Collector ich nie usuwa. W końcu GDI + pomyśli, że kończy Ci się pamięć i wyrzuci wyjątek „Parametr jest nieprawidłowy”. DzwonienieGC.Collect()
co jakiś czas (niezbyt często!) Wydaje się rozwiązywać ten problem.źródło