Co jest złego w używaniu GC.Collect ()?

103

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.

Pułapka
źródło
24
„najmniejsza zmiana w przebiegu programu może przynieść katastrofalne konsekwencje ... ktoś może umrzeć” - czy na pewno C # .NET jest wystarczająco deterministyczny dla Twoich celów?
Steve Jessop,
4
Ani Windows, ani .NET nie są platformami czasu rzeczywistego i dlatego nie można zagwarantować wskaźników wydajności, a przynajmniej nie wystarczających do narażania życia ludzkiego. Zgadzam się z nikim, że albo przesadzasz, albo nieostrożnie.
Sergio Acosta
3
LOL w „jednej z tych rzeczy, których szanowani programiści nigdy by nie używali, nawet ci, którzy nawet nie wiedzą, do czego służy”! W mojej książce programiści, którzy używają rzeczy nie wiedząc dlaczego, nie są najbardziej szanowani. :)
The Dag

Odpowiedzi:

87

Z bloga Rico ...

Zasada nr 1

Nie.

To naprawdę najważniejsza zasada. Można śmiało powiedzieć, że większość zastosowań GC.Collect () to zły pomysł i włożyłem w to trochę szczegółów w oryginalnym poście, więc nie będę tego tutaj powtarzał. Przejdźmy więc do ...

Zasada nr 2

Rozważ wywołanie GC.Collect (), jeśli właśnie wydarzyło się jakieś jednorazowe zdarzenie i jest wysoce prawdopodobne, że spowodowało śmierć wielu starych obiektów.

Klasycznym tego przykładem jest sytuacja, gdy piszesz aplikację kliencką i wyświetlasz bardzo duży i skomplikowany formularz, który ma wiele powiązanych z nim danych. Twój użytkownik właśnie wszedł w interakcję z tym formularzem, potencjalnie tworząc duże obiekty ... rzeczy, takie jak dokumenty XML lub duży zestaw danych lub dwa. Kiedy formularz zamyka się, te obiekty są martwe, więc GC.Collect () odzyska pamięć z nimi związaną ...

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.

Zasada nr 1 powinna być ważniejsza od reguły nr 2 bez mocnych dowodów.

Mierz, mierz, mierz.

Jon Norton
źródło
9
Powiedziałbym, że to tylko stara rzecz. Nic nie jest naprawdę złe ani niebezpieczne, jeśli wiesz, co robisz, a zatem wiesz, kiedy i jak to zrobić, a także skutki uboczne. Takie rzeczy jak „Don't never, ever use xxxx” mają chronić świat przed kiepskimi programistami: D
Jorge Córdoba
zobacz także stackoverflow.com/questions/233596/…
Ian Ringrose
Nie twierdzę, że używanie GC.Collect jest dobrą praktyką. Ale czasami jest to szybki sposób na rozwiązanie problemów bez znajomości ich prawdziwej przyczyny. Wiem, że jest brzydki, ale działa i wydaje mi się, że nie jest to złe podejście, zwłaszcza gdy nie ma czasu na ustalenie pierwotnej przyczyny problemu, a szef stoi za tobą ... wiesz.
Silent Sojourner
58

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

Aaron Fischer
źródło
3
To prawda, ale nie wiem, czy mogliby przyjąć założenia, które mają zastosowanie do wszystkich wydarzeń.
MasterMastic
2
@Ken Nie, nie mogą. Ale czy jesteś w lepszej sytuacji, aby to zrobić? A może zamierzasz napisać kod, zakładając określony sprzęt, określoną wersję systemu operacyjnego i tak dalej? W tym przypadku stosunek bólu do wzmocnienia jest zbyt wysoki.
The Dag
2
@TheDag IMO oczywiście, że jestem. Kiedy zwalniam pamięć i tak dalej, nie obchodzi mnie sprzęt, ponieważ jest to zadanie systemu operacyjnego, które sobie z tym radzi. Nie obchodzi mnie również system operacyjny, ponieważ mam interfejs wspólny dla wszystkich, dla których programuję. (np. nie obchodzi mnie, czy to Windows, Mac czy Linux: kiedy alokuję / zwalniam pamięć w C / C ++ jest to nowe / delete malloc / dealloc). Zawsze mogę się mylić, więc nie krępuj się mnie poprawić.
MasterMastic
@MasterMastic mallocma 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ć. Gdyby mallocbył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.).
Luaan
24

A co powiesz na używanie obiektów COM, takich jak MS Word lub MS Excel z .NET? Bez wywoływania GC.Collectpo zwolnieniu obiektów COM stwierdziliśmy, że instancje aplikacji Word lub Excel nadal istnieją.

W rzeczywistości kod, którego używamy, to:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

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„s Collectmetoda jeszcze Public?

Dib
źródło
3
To stanowiłoby świetne nowe pytanie dotyczące StackOverflow, tj .: jak wyeliminować wystąpienia COM bez wywoływania GC. Ze szczególnym uwzględnieniem niezarządzanych odwołań cyklicznych. Jest to jedno z wyzwań, które sprawiło, że obawiałem się uaktualnienia mojego dodatku VB6 do programu Outlook do C #. (Włożyliśmy dużo pracy, aby opracować wzorce kodowania i przypadki testowe po stronie VB, które gwarantowały, że odniesienia COM zostały zabite w sposób deterministyczny, gdy nie były już potrzebne).
rkagerer
2
Jeśli dotyczy to ogólnie obiektów COM, być może jest to prawidłowy scenariusz. Ale od razu powiedziałbym, że problem jest prawdopodobnie związany z używaniem aplikacji klienckiej zaprojektowanej dla interaktywnego pulpitu jako serwera COM. Z bazy wiedzy MSDN: „Firma Microsoft obecnie nie zaleca i nie obsługuje automatyzacji aplikacji Microsoft Office z żadnej nienadzorowanej, nieinteraktywnej aplikacji klienckiej lub składnika (w tym usług ASP, ASP.NET, DCOM i NT), ponieważ pakiet Office może wykazywać niestabilne zachowanie i / lub zakleszczenie, gdy pakiet Office jest uruchamiany w tym środowisku ”.
The Dag
2
@TheDag - Microsoft może nie polecać, ale wielu z nas musiało przenieść stary kod VB6 z interfejsem biurowym do aplikacji Windows .Net. Spędziłem miesiące pracy, aż w końcu pozbyłem się wszystkich niewidocznych zawieszonych odniesień do dużego projektu konwersji VB6 na .Net. Pomogło jednak nauczenie się wydawania w odwrotnej kolejności przypisywania i trzymanie lokalnych referencji do KAŻDEGO pojedynczego obiektu com, w tym kolekcji.
Dib
15

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, Collectchyba ż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.Collectstwierdzeń. 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żywanie IDisposablena 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.

Jason Short
źródło
Winowajcą są często wydarzenia. Za każdym razem, gdy metoda wystąpienia jest używana jako procedura obsługi zdarzenia, wydawca zdarzenia ma odwołanie do subskrybenta za pośrednictwem delegata zdarzenia. Jedynym „łatwym” sposobem uniknięcia problemów jest używanie tylko wydawców, którzy są co najwyżej tak długowieczni jak subskrybenci (np. TextBox publikujący zdarzenie obsługiwane przez formularz zawierający nie stanowi problemu, ponieważ pole tekstowe nie jest przypuszczalne żyć poza formą). Przykładowy scenariusz problemu: model singleton, tymczasowe widoki obsługujące zdarzenia modelu.
The Dag
5
Jak można schrzanić całą maszynę?
Adam R. Grey
13

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.

rjohnston
źródło
12

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

TheZenker
źródło
10

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.

TraumaPony
źródło
9

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

mądry
źródło
Prawdziwe. Ale testy automatyczne zdolne do wychwycenia warunku błędu „obiekt nie kwalifikuje się do czyszczenia pamięci, ale powinien być” byłyby cenne. Można to osiągnąć poprzez połączenie logiki fabrycznej, logiki destruktora i GC.Collect. Np. Twoja klasa Entity ma właściwość IObjectTracker, zwykle null, ale przypisaną przez fabrykę jednostek do celów testowych. Fabryka powiadamia również urządzenie śledzące o narodzinach obiektu, a destruktor powiadamia go (jeśli jest obecny) o śmierci. Jeśli wiesz, że „destruktor wykonał działanie dla wszystkich obiektów, które można zbierać śmieci”, możesz sprawdzić stan modułu śledzącego, aby wykryć wycieki.
The Dag
7

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.

Ta01
źródło
2
Nie tylko powodujesz spacer po stosie, ale także główny wątek aplikacji (i wszystkie utworzone przez niego wątki potomne) są zamrożone, aby GC mógł przejść przez stos. Im więcej czasu Twoja aplikacja spędza w GC, tym więcej czasu spędza zamrożona.
Scott Dorman
3
Bardziej martwi mnie awaria aplikacji z powodu wyjątku braku pamięci niż niska wydajność, ponieważ aplikacja / GC wyrzucała elementy, które nie są już potrzebne. Czy ktoś wie, dlaczego Microsoft wydaje się rzucać wyjątek OOM bez PIERWSZEGO wyrzucania śmieci? (Bez tego oczywistym krokiem - lub przynajmniej wyjaśnienie dlaczego krok ten nie wydaje się być próba przed wyrzuceniem OOM wyjątek nie jestem pewien mam żadnej wiary w rzeczy dzieją się „automatycznie” w „sposób, jak powinny.”
Wonderbird
6

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:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).
Venugopal M
źródło
5

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 a WeakReference, 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.Collectmoż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, jak GC.Collectnie został wywołany, ale system zgromadzi trochę śmieci, powodując, że kolejne zbieranie będzie wymagane wcześniej niż GC.Collectnie został wezwany.

Czasy, które widzę GC.Collectnaprawdę 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ć, że GC.Collectwywoł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ąć.

supercat
źródło
5

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.

dołek
źródło
2
Powołaniem Disposejest nie powinien zwolnić zarządzanej pamięci. Wydaje się, że nie wiesz, jak działa model pamięci w .NET.
Andrew Barber
4

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

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Poprzez przeniesienie wywołania GC poza zakres podprogramu, śmieci zostały zebrane, a pamięć została zwolniona.

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

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

Paul Smith
źródło
4

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

Gerard ONeill
źródło
2

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

Sergio Acosta
źródło
2
Ciekawe, ale dokumentacja mówi, że AddMemoryPressure powinno być używane, gdy „mały obiekt zarządzany przydziela dużą ilość niezarządzanej pamięci”. (moje podkreślenie)
Robert Paulson
2

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.

Obrabować
źródło
1
Heurystyczna natura garbage collectora i fakt, że ujawnili tę funkcjonalność światu zewnętrznemu, sprawiają, że myślę o tym jako o czymś, co jest przydatne, jeśli jest używane tam, gdzie trzeba. Problemem nie jest używanie go, ale wiedza, jak, gdzie i kiedy go używać.
Trap
Nie wspominając o lepszej wiedzy GC o każdej innej aplikacji i ich potrzebach w zakresie pamięci. GC negocjuje pamięć z systemem operacyjnym i jako taka ma wpływ na dostępną pamięć fizyczną i wszystkie inne procesy na komputerze, zarówno zarządzane, jak i niezarządzane. Chociaż wątpię, że GC naprawdę wie, „kiedy jest dobry czas na zbieranie danych” na podstawie „każdego przypadku z osobna”, jest bardzo prawdopodobne, że ma ogólnie lepszą strategię niż ... JAKIEKOLWIEK pojedyncza aplikacja. ;)
The Dag
2

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.

Sam
źródło
5
to być może uogólnienie
MickyD
1

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

Christopher Elliott
źródło
1

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.

Scott Dorman
źródło
Mógłby korzystać z platformy .Net Micro Framework, która została zaprojektowana dla środowisk czasu rzeczywistego.
TraumaPony
@TraumaPony: Sprawdź tabelę na dole tej strony msdn.microsoft.com/en-us/embedded/bb278106.aspx : Najwyraźniej Micro Framework nie został zaprojektowany do środowisk czasu rzeczywistego. Został jednak zaprojektowany dla środowisk wbudowanych (takich jak WinCE), ale z mniejszymi wymaganiami dotyczącymi zasilania.
Scott Dorman
1

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.

FastAl
źródło