Chociaż rozumiem poważne konsekwencje grania tą funkcją (a przynajmniej tak myślę), nie rozumiem, dlaczego staje się ona jedną z tych rzeczy, których szanowani programiści nigdy by nie używali, nawet ci, którzy nawet nie wiedzą po co to jest.
Powiedzmy, że tworzę aplikację, w której użycie pamięci jest bardzo różne w zależności od tego, co robi użytkownik. Cykl życia aplikacji można podzielić na dwa główne etapy: edycja i przetwarzanie w czasie rzeczywistym. Załóżmy, że na etapie edycji powstają miliardy, a nawet biliony obiektów; niektóre z nich są małe, a niektóre nie, niektóre mogą mieć finalizatory, a niektóre mogą nie, i przypuszcza się, że ich żywotność waha się od kilku milisekund do długich godzin. Następnie użytkownik decyduje się przejść do etapu czasu rzeczywistego. W tym miejscu załóżmy, że wydajność odgrywa fundamentalną rolę, a najmniejsza zmiana w przebiegu programu może przynieść katastrofalne skutki. Tworzenie obiektów jest następnie zredukowane do minimum przy użyciu pul obiektów i tym podobnych, ale potem GC nieoczekiwanie dzwoni i odrzuca wszystko, a ktoś umiera.
Pytanie: czy w takim przypadku nie byłoby mądrze wywołać GC.Collect () przed przejściem do drugiego etapu?
W końcu te dwa etapy nigdy się nie pokrywają w czasie, a cała optymalizacja i statystyki, które mógłby zebrać GC, byłyby tu mało przydatne ...
Uwaga: Jak niektórzy z was zauważyli, .NET może nie być najlepszą platformą dla takiej aplikacji, ale to wykracza poza zakres tego pytania. Celem jest wyjaśnienie, czy wywołanie GC.Collect () może poprawić ogólne zachowanie / wydajność aplikacji, czy nie. Wszyscy zgadzamy się, że okoliczności, w których zrobiłbyś coś takiego, są niezwykle rzadkie, ale z drugiej strony GC próbuje odgadnąć i robi to doskonale przez większość czasu, ale nadal chodzi o zgadywanie.
Dzięki.
Odpowiedzi:
Z bloga Rico ...
Wygląda więc na to, że ta sytuacja może podlegać zasadzie nr 2, wiesz, że jest taki moment, w którym zginęło wiele starych obiektów i to się nie powtarza. Nie zapomnij jednak o pożegnalnych słowach Rico.
Mierz, mierz, mierz.
źródło
Jeśli wywołasz GC.Collect () w kodzie produkcyjnym, zasadniczo deklarujesz, że wiesz więcej niż autorzy GC. Tak może być. Jednak zwykle tak nie jest i dlatego zdecydowanie odradza się.
źródło
malloc
ma tylko bardzo prosty interfejs, a jego implementacje mogą się różnić na tyle, by mieć znaczenie. Wszystko zależy od rodzaju problemu, który próbujesz rozwiązać. Gdybymalloc
było „wystarczająco dobre”, nie potrzebowałbyś puli buforów, prawda? Programowanie w C / C ++ jest pełne przykładów, w których próbujesz odgadnąć system operacyjny / środowisko wykonawcze / biblioteki, ponieważ wiesz lepiej (a czasami naprawdę wiesz). Wiele aplikacji o krytycznym znaczeniu dla wydajności całkowicie unika używania alokatorów systemowych / wykonawczych. Gry używane do wstępnego przydzielania całej pamięci podczas uruchamiania (tablice o stałej wielkości itp.).A co powiesz na używanie obiektów COM, takich jak MS Word lub MS Excel z .NET? Bez wywoływania
GC.Collect
po zwolnieniu obiektów COM stwierdziliśmy, że instancje aplikacji Word lub Excel nadal istnieją.W rzeczywistości kod, którego używamy, to:
Czy byłoby to więc niewłaściwe użycie garbage collectora? Jeśli tak, w jaki sposób sprawimy, że obiekty Interop umrą? Także, jeśli nie ma być używany tak, Dlaczego
GC
„sCollect
metoda jeszczePublic
?źródło
Cóż, GC jest jedną z tych rzeczy, z którymi mam związek miłości / nienawiści. Mamy złamane w przeszłości przez VistaDB i napisał o nim. Naprawili to, ale uzyskanie od nich poprawek w takich sprawach zajmuje DŁUGO.
GC jest złożona, a podejście uniwersalne dla wszystkich jest bardzo, bardzo trudne do wykonania na czymś tak dużym. MS wykonało całkiem dobrą robotę, ale czasami można oszukać GC.
Generalnie nie powinieneś dodawać a,
Collect
chyba że wiesz na pewno, że właśnie zrzuciłeś tonę pamięci i dojdzie do kryzysu wieku średniego, jeśli GC nie wyczyści tego teraz.Możesz schrzanić całą maszynę serią złych
GC.Collect
stwierdzeń. Potrzeba wydania instrukcji zbiorczej prawie zawsze wskazuje na większy podstawowy błąd. Wyciek pamięci ma zwykle związek z odniesieniami i brakiem zrozumienia, jak one działają. Lub używanieIDisposable
na obiektach, które tego nie potrzebują i znacznie większe obciążenie GC.Uważnie obserwuj procent czasu spędzonego w GC za pomocą liczników wydajności systemu. Jeśli zauważysz, że Twoja aplikacja wykorzystuje 20% lub więcej czasu w GC, masz poważne problemy z zarządzaniem obiektami (lub nienormalny wzorzec użycia). Chcesz zawsze zminimalizować czas spędzany przez GC, ponieważ przyspieszy to całą Twoją aplikację.
Należy również zauważyć, że GC różni się na serwerach niż na stacjach roboczych. Widziałem wiele małych, trudnych do wyśledzenia problemów, gdy ludzie nie testowali ich obu (lub nawet nie byli świadomi, że jest ich dwoje).
Aby być tak wyczerpującym, jak to tylko możliwe, powinieneś również przetestować pod Mono, jeśli celujesz również w tę platformę. Ponieważ jest to zupełnie inna implementacja, może wystąpić zupełnie inne problemy niż wdrożenie MS.
źródło
Są sytuacje, w których jest to przydatne, ale generalnie należy tego unikać. Mógłbyś to porównać do GOTO, czyli jazdy na motorowerze: robisz to kiedy potrzebujesz, ale nie mówisz o tym znajomym.
źródło
Z mojego doświadczenia wynika, że nigdy nie było wskazane wywoływanie GC.Collect () w kodzie produkcyjnym. W debugowaniu tak, ma to swoje zalety, aby pomóc wyjaśnić potencjalne wycieki pamięci. Myślę, że moim podstawowym powodem jest to, że GC został napisany i zoptymalizowany przez programistów znacznie mądrzejszych ode mnie, a jeśli dojdę do punktu, w którym czuję, że muszę wywołać GC.Collect (), jest to wskazówka, że zboczyłem ze ścieżki gdzieś. W twojej sytuacji nie wygląda na to, że faktycznie masz problemy z pamięcią, po prostu obawiasz się, jaką niestabilność wniesie kolekcja do twojego procesu. Widząc, że nie wyczyści obiektów, które nadal są w użyciu i że bardzo szybko dostosowuje się zarówno do rosnących, jak i spadających wymagań, myślę, że nie będziesz musiał się tym martwić.
źródło
Jednym z największych powodów wywołania GC.Collect () jest fakt, że właśnie wykonałeś ważne wydarzenie, które tworzy mnóstwo śmieci, takich jak to, co opisujesz. Wywołanie GC.Collect () może być tutaj dobrym pomysłem; w przeciwnym razie GC może nie zrozumieć, że było to zdarzenie „jednorazowe”.
Oczywiście powinieneś go sprofilować i przekonać się sam.
źródło
Cóż, oczywiście nie powinieneś pisać kodu z wymaganiami czasu rzeczywistego w językach z wyrzucaniem elementów bezużytecznych w czasie innym niż rzeczywisty.
W przypadku dobrze zdefiniowanych etapów nie ma problemu z uruchomieniem garbage-collectora. Ale ten przypadek jest niezwykle rzadki. Problem polega na tym, że wielu programistów będzie próbowało wykorzystać to do rozwiązywania problemów na papierze w stylu kultowego cargo, a dodanie tego bezkrytycznie spowoduje problemy z wydajnością.
źródło
Wywołanie GC.Collect () zmusza środowisko CLR do wykonania spaceru po stosie, aby sprawdzić, czy każdy obiekt może być rzeczywiście zwolniony, sprawdzając odwołania. Wpłynie to na skalowalność, jeśli liczba obiektów jest wysoka, a także zbyt często wyzwalanie czyszczenia pamięci. Zaufaj środowisku CLR i pozwól, aby moduł wyrzucania elementów bezużytecznych działał w razie potrzeby.
źródło
W rzeczywistości nie uważam, że dzwonienie do GC.Collect jest bardzo złą praktyką.
Może się zdarzyć, że będziemy tego potrzebować. Na przykład mam formularz, który uruchamia wątek, który następnie otwiera różne tabele w bazie danych, wyodrębnia zawartość pola BLOB do pliku tymczasowego, szyfruje plik, a następnie wczytuje plik do strumienia binarnego iz powrotem do BLOBa pole w innej tabeli.
Cała operacja zajmuje sporo pamięci i nie ma pewności co do liczby wierszy i rozmiaru zawartości plików w tabelach.
Kiedyś często otrzymywałem wyjątek OutofMemory i pomyślałem, że rozsądnie byłoby okresowo uruchamiać GC.Collect na podstawie zmiennej licznika. Zwiększam licznik i po osiągnięciu określonego poziomu wywoływany jest GC w celu zebrania wszelkich śmieci, które mogły powstać, i odzyskania pamięci utraconej z powodu nieprzewidzianych wycieków pamięci.
Po tym myślę, że działa dobrze, przynajmniej bez wyjątku !!!
Wzywam w następujący sposób:
źródło
W przypadku .net czas wymagany do przeprowadzenia czyszczenia pamięci jest znacznie silniej powiązany z ilością rzeczy, które nie są śmieciami, niż z ilością rzeczy, które są. Rzeczywiście, chyba że obiekt nadpisuje
Finalize
(jawnie lub za pośrednictwem destruktora C #), jest celem aWeakReference
, znajduje się na stosie dużych obiektów lub jest specjalny w inny sposób związany z GC, jedyną rzeczą identyfikującą pamięć, w której znajduje się bycie obiektem oznacza istnienie zakorzenionych odniesień do niego. W przeciwnym razie działanie KG jest analogiczne do zabierania z budynku wszystkiego, co wartościowe, i zdynamizowania budynku, budowania nowego na miejscu starego i umieszczania w nim wszystkich wartościowych przedmiotów. Wysiłek potrzebny do dynamitowania budynku jest całkowicie niezależny od ilości śmieci w nim zawartych.W konsekwencji dzwonienie
GC.Collect
może zwiększyć ogólną ilość pracy, jaką musi wykonać system. Spowoduje to opóźnienie wystąpienia następnej kolekcji, ale prawdopodobnie wykona od razu tyle samo pracy, ile wymagałoby to następnej kolekcji w momencie jej wystąpienia; w momencie, w którym nastąpiłoby następne zbieranie, całkowity czas spędzony na zbieraniu będzie mniej więcej taki sam, jakGC.Collect
nie został wywołany, ale system zgromadzi trochę śmieci, powodując, że kolejne zbieranie będzie wymagane wcześniej niżGC.Collect
nie został wezwany.Czasy, które widzę
GC.Collect
naprawdę przydatne, są wtedy, gdy trzeba zmierzyć użycie pamięci przez jakiś kod (ponieważ dane dotyczące użycia pamięci są naprawdę znaczące tylko po kolekcji) lub określić, który z kilku algorytmów jest lepszy (wywołanie GC.Collect () przed uruchomieniem każdego z kilku fragmentów kodu może pomóc zapewnić spójny stan bazowy). Jest kilka innych przypadków, w których można wiedzieć rzeczy, których GC nie zna, ale jeśli nie piszemy programu jednowątkowego, nie można się dowiedzieć, żeGC.Collect
wywołanie pomogłoby strukturom danych jednego wątku uniknąć „kryzysu wieku średniego „nie spowodowałoby, że dane innych wątków miałyby„ kryzysy wieku średniego ”, których w innym przypadku można by uniknąć.źródło
Tworzenie obrazów w pętli - nawet jeśli wywołasz polecenie dispose, pamięć nie zostanie odzyskana. Za każdym razem zbiera się śmieci. Przeszedłem z 1,7 GB pamięci w mojej aplikacji do przetwarzania zdjęć do 24 MB, a wydajność jest doskonała.
Jest absolutnie czas, aby zadzwonić do GC.Collect.
źródło
Dispose
jest nie powinien zwolnić zarządzanej pamięci. Wydaje się, że nie wiesz, jak działa model pamięci w .NET.Mieliśmy podobny problem z garbage collector, który nie zbierał śmieci i zwalniał pamięć.
W naszym programie przetwarzaliśmy niektóre arkusze kalkulacyjne Excel o niewielkich rozmiarach za pomocą OpenXML. Arkusze kalkulacyjne zawierały od 5 do 10 „arkuszy” z około 1000 rzędami po 14 kolumn.
Program w środowisku 32-bitowym (x86) wywalał się z błędem „braku pamięci”. Udało nam się uruchomić go w środowisku x64, ale chcieliśmy lepszego rozwiązania.
Znaleźliśmy jednego.
Oto kilka uproszczonych fragmentów kodu, które nie zadziałały i co zadziałało, jeśli chodzi o jawne wywołanie modułu Garbage Collector w celu zwolnienia pamięci z usuniętych obiektów.
Wywołanie GC z wnętrza podprogramu nie zadziałało. Pamięć nigdy nie została odzyskana ...
Poprzez przeniesienie wywołania GC poza zakres podprogramu, śmieci zostały zebrane, a pamięć została zwolniona.
Mam nadzieję, że pomoże to innym, którzy są sfrustrowani wyrzucaniem elementów bezużytecznych .NET, gdy wydaje się, że ignorują wywołania
GC.Collect()
.Paul Smith
źródło
Nie ma nic złego w jawnym wezwaniu kolekcji. Niektórzy ludzie naprawdę chcą wierzyć, że jeśli jest to usługa świadczona przez sprzedawcę, nie kwestionuj tego. Aha, i wszystkie te przypadkowe zawieszanie się w niewłaściwych momentach aplikacji interaktywnej? Następna wersja sprawi, że będzie lepiej!
Pozwalanie procesowi w tle zajmować się manipulacją pamięcią oznacza, że nie musimy sobie z tym radzić, to prawda. Nie oznacza to jednak logicznie, że najlepiej byłoby, gdybyśmy sami nie zajmowali się tym problemem w każdych okolicznościach. GC jest zoptymalizowany dla większości przypadków. Ale nie oznacza to logicznie, że jest zoptymalizowany we wszystkich przypadkach.
Czy kiedykolwiek odpowiedziałeś na otwarte pytanie, takie jak „jaki jest najlepszy algorytm sortowania”, udzielając ostatecznej odpowiedzi? Jeśli tak, nie dotykaj GC. Ci z Was, którzy pytali o warunki lub udzielali odpowiedzi typu „w tym przypadku”, mogą kontynuować naukę o GC i kiedy ją aktywować.
Muszę powiedzieć, że miałem zawieszone aplikacje w Chrome i Firefoksie, które cholernie mnie frustrują, a nawet wtedy w niektórych przypadkach pamięć rośnie bez przeszkód - gdyby tylko nauczyli się dzwonić do śmieciarza - lub dał mi tak, że gdy zacznę czytać tekst strony, mogę w nią kliknąć i dzięki temu przez następne 20 minut nie będzie zawieszał się.
źródło
Myślę, że masz rację co do scenariusza, ale nie jestem pewien co do interfejsu API.
Microsoft twierdzi, że w takich przypadkach należy dodać obciążenie pamięci jako wskazówkę do GC, że wkrótce powinien wykonać kolekcję.
źródło
Co jest z tym nie tak? Fakt, że po raz drugi zgadujesz moduł wyrzucania elementów bezużytecznych i alokator pamięci, które między nimi mają znacznie większe pojęcie o rzeczywistym wykorzystaniu pamięci przez aplikację w czasie wykonywania niż Ty.
źródło
Chęć wywołania GC.Collect () zazwyczaj polega na próbowaniu zatuszowania błędów popełnionych gdzie indziej!
Byłoby lepiej, gdybyś znalazł miejsce, w którym zapomniałeś wyrzucić rzeczy, których już nie potrzebujesz.
źródło
Podsumowując, możesz sprofilować aplikację i zobaczyć, jak te dodatkowe kolekcje wpływają na rzeczy. Sugerowałbym jednak trzymanie się z daleka od tego, chyba że zamierzasz profilować. GC został zaprojektowany tak, aby sam o siebie dbał, a wraz z rozwojem środowiska wykonawczego może zwiększać wydajność. Nie chcesz, aby kręcić się w pobliżu mnóstwo kodu, który może zepsuć pracę i nie być w stanie skorzystać z tych ulepszeń. Istnieje podobny argument przemawiający za używaniem foreach zamiast for, ponieważ przyszłe ulepszenia w ramach okładek można dodać do foreach, a kod nie musi się zmieniać, aby skorzystać.
źródło
Sama .NET Framework nigdy nie została zaprojektowana do działania w środowisku czasu rzeczywistego. Jeśli naprawdę potrzebujesz przetwarzania w czasie rzeczywistym, możesz użyć wbudowanego języka czasu rzeczywistego, który nie jest oparty na .NET lub użyć .NET Compact Framework działającego na urządzeniu z systemem Windows CE.
źródło
Najgorsze jest to, że program na chwilę się zawiesi. Więc jeśli ci to nie przeszkadza, zrób to. Zwykle nie jest to potrzebne w przypadku grubego klienta lub aplikacji internetowych, w których występuje głównie interakcja z użytkownikiem.
Zauważyłem, że czasami programy z długo działającymi wątkami lub programy wsadowe otrzymują wyjątek OutOfMemory, mimo że prawidłowo usuwają obiekty. Jeden, który pamiętam, dotyczył przetwarzania transakcji w bazie danych biznesowych; drugą była procedura indeksowania w wątku w tle w grubej aplikacji klienckiej.
W obu przypadkach wynik był prosty: brak GC.Collect, z pamięci, konsekwentnie; GC.Collect, bezbłędna wydajność.
Próbowałem tego kilka razy, aby rozwiązać problemy z pamięcią, ale bezskutecznie. Wyciągnąłem to.
Krótko mówiąc, nie umieszczaj go, chyba że otrzymujesz błędy. Jeśli włożysz go i nie rozwiąże to problemu z pamięcią, wyjmij go z powrotem. Pamiętaj, aby przetestować w trybie wydania i porównać jabłka z jabłkami.
Jedyny przypadek, kiedy coś może pójść nie tak, jest wtedy, gdy podejmiesz moralizm. To nie jest kwestia wartości; wielu programistów zmarło i poszło prosto do nieba z wieloma niepotrzebnymi GC. zbiera w swoim kodzie, który ich przeżywa.
źródło