Kiedy można dzwonić do GC.Collect?

166

Ogólna rada jest taka, że ​​nie powinieneś wywoływać GC.Collectz 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?

Brian Rasmussen
źródło
Możliwy duplikat najlepszej praktyki
wymuszania zbierania elementów bezużytecznych

Odpowiedzi:

153

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.LowLatencyi GCLatencyMode.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.TryStartNoGCRegionmetoda (używana do ustawiania wartości tylko do odczytu GCLatencyMode.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ć:

Jon Skeet
źródło
8
Według kodu źródłowego MS wywoływanie GC.Collect (2) co 850 ms jest w porządku. Nie wierzysz? Następnie wystarczy zapoznać się z PresentationCore.dll, MS.Internal.MemoryPressure.ProcessAdd (). Obecnie mam aplikację do przetwarzania obrazu (małe obrazy, nic z prawdziwym obciążeniem pamięci), w której wywołanie GC.Collect (2) trwa dłużej niż 850 ms, więc cała aplikacja zostaje przez to zamrożona (aplikacja spędza 99,7% czasu w GC).
springy76
36
@ springy76: wykonywana przez Microsoft w jednym miejscu, nie oznacza to uznane za coś dobrego przez te udzielanie porad z Microsoft ...
Jon Skeet
4
Nie podoba mi się ten przykład. Po co to robić po zamknięciu formy? Jednym z dobrych przykładów, które widzę, jest po załadowaniu poziomu gry na XBox lub WindowsPhone. Na tych platformach GC działa po przydzieleniu 1MB lub czegoś podobnego. Dlatego dobrze jest przeznaczyć jak najwięcej podczas ładowania poziomu (podczas wyświetlania ekranu powitalnego), a następnie wykonać GC.Collect, aby spróbować uniknąć zbierania w trakcie gry.
Piotr Perak
8
@Peri: Celem zrobienia tego po zamknięciu formularza jest to, że właśnie utworzyłeś grupę obiektów (kontrolek, danych, które wyświetlałeś) kwalifikujących się do wyrzucania elementów bezużytecznych - więc dzwoniąc GC.Collect, w zasadzie mówisz odśmiecaczowi, że znasz lepiej niż to dla odmiany. Dlaczego nie podoba ci się przykład?
Jon Skeet,
6
@SHCJ: 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.
Jon Skeet,
50

Używam GC.Collecttylko podczas pisania surowych zestawów testowych wydajności / profilera; czyli mam dwa (lub więcej) bloki kodu do przetestowania - coś takiego:

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

Tak więc TestA()i TestB()biegnij z możliwie podobnym stanem - tj. TestB()Nie zostaje uderzony tylko dlatego, że TestAzostawił go bardzo blisko punktu krytycznego.

Klasycznym przykładem może być prosty plik exe konsoli ( Mainmetoda sortowania - na przykład wystarczająca do opublikowania tutaj), który pokazuje różnicę między zapętloną konkatenacją ciągów a StringBuilder.

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

Marc Gravell
źródło
1
I prawdopodobnie dodam też „WaitForPendingFinalizers” (lub cokolwiek to jest) w tym przypadku ;-p
Marc Gravell
29

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

  • Otrzymuje listę nazw plików w wierszu poleceń
  • Przetwarza pojedynczy plik, a następnie zapisuje wynik do pliku wyników.
  • Podczas przetwarzania pliku tworzy wiele powiązanych ze sobą obiektów, których nie można zebrać do czasu zakończenia przetwarzania pliku (np. Drzewo parsowania)
  • Nie zachowuje stanu dopasowania między przetwarzanymi plikami .

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

Jedynym przypadkiem, w którym rozważałbym wymuszenie kolekcji, jest sytuacja, w której wiem, że ostatnio utworzono wiele obiektów i obecnie istnieją odwołania do bardzo niewielu obiektów.

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

Ian Ringrose
źródło
19

Jeden przypadek ma miejsce, gdy próbujesz przetestować kod jednostkowy, który używa WeakReference .

Brian
źródło
11

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:

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

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.

Paul Ruane
źródło
Widziałem teraz kolekcję dwuprzebiegową w kilku miejscach, ale po przeczytaniu fragmentu w dokumentacji MSDN dla GC.WaitForPendingFinalizers, który mówi: „Poczekaj na zakończenie wszystkich finalizatorów przed kontynuowaniem. Bez tego wywołania GC.WaitForPendingFinalizers, poniższa pętla procesu roboczego może być wykonywana w tym samym czasie, co finalizatory. W przypadku tego wywołania pętla procesu roboczego jest wykonywana dopiero po wywołaniu wszystkich finalizatorów. " Jestem tylko odrobiną paranoi. Czy znasz ostateczne źródło wykonywania dwóch przejść?
jerhewet
1
@jerhewet: Kluczem do zrozumienia, dlaczego wymagane są dwie kolekcje, jest zrozumienie, co dzieje się z obiektami z finalizatorami. Niestety nie mam dokładnie tego, o co prosisz, ale przeczytaj ten artykuł i to pytanie na SO .
Paul Ruane
10

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

Joel Coehoorn
źródło
8

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.

Etos
źródło
7

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

M4N
źródło
3
Już tam byłem. Nie szukam wymówek dla zrobienia czegoś, czego nie powinieneś robić, ale chciałbym wiedzieć, czy są jakieś szczególne przypadki, w których byłoby to do przyjęcia.
Brian Rasmussen
5

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:

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

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.

garthhh
źródło
tak, natknąłem się na to z dostępem do .net w programie Excel, który działa również przez obiekty com. Należy zauważyć, że nie będzie to działać dobrze w trybie DEBUG, ponieważ operacje GC są tam ograniczone. Będzie działać zgodnie z przeznaczeniem tylko w trybie RELEASE. odpowiedni link: stackoverflow.com/questions/17130382/…
Blechdose
5

Robiłem testy wydajności na tablicy i liście:

private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
    var lstNumbers = new List<int>();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List<int>\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

i dostałem OutOfMemoryExceptionw metodzie GetSomeNumbers_Enumerable_Range jedynym obejściem jest zwolnienie pamięci przez:

numbers = null;
GC.Collect();
Daniel B.
źródło
Dlaczego głosowanie w dół? moja odpowiedź to przykład, który pokazuje, kiedy zadzwonić do GC. Masz lepszą propozycję? Zapraszamy do prezentacji.
Daniel B,
4

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

casperOne
źródło
3

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:

WeakReference w = CodeThatShouldNotMemoryLeak();
Assert.IsTrue(w.IsAlive);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsFalse(w.IsAlive);

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.

ChaseMedallion
źródło
2

Krótka odpowiedź brzmi: nigdy!

BankZ
źródło
9
Nawet w tej sytuacji ?
Roman Starkov
2
using(var stream = new MemoryStream())
{
   bitmap.Save(stream, ImageFormat.Png);
   techObject.Last().Image = Image.FromStream(stream);
   bitmap.Dispose();

   // Without this code, I had an OutOfMemory exception.
   GC.Collect();
   GC.WaitForPendingFinalizers();
   //
}
Denis
źródło
2

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.

Carl Looper
źródło
2

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:

public static TResult ExecuteOOMAware<T1, T2, TResult>(Func<T1,T2 ,TResult> func, T1 a1, T2 a2)
{

    int oomCounter = 0;
    int maxOOMRetries = 10;
    do
    {
        try
        {
            return func(a1, a2);
        }
        catch (OutOfMemoryException)
        {
            oomCounter++;
            if (maxOOMRetries > 10)
            {
                throw;
            }
            else
            {
                Log.Info("OutOfMemory-Exception caught, Trying to fix. Counter: " + oomCounter.ToString());
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(oomCounter * 10));
                GC.Collect();
            }
        }
    } while (oomCounter < maxOOMRetries);

    // never gets hitted.
    return default(TResult);
}

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

Thomas Haller
źródło
Komponent nie powinien dzwonić GC.Collect. Ponieważ ma to wpływ na całą aplikację, tylko aplikacja powinna to robić (jeśli w ogóle).
CodesInChaos
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.
G. Stoynev
2

Powinieneś unikać używania GC.Collect (), ponieważ jest bardzo drogi. Oto przykład:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet.Remove(RecordSet[0]);

        }
        GC.Collect(); // AVOID
    }

WYNIK TESTU: WYKORZYSTANIE PROCESORA 12%

Kiedy zmienisz na to:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet[0].Dispose(); //  Bitmap destroyed!
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet[0].Dispose(); //  Bitmap destroyed!
            RecordSet.Remove(RecordSet[0]);

        }
        //GC.Collect();
    }

WYNIK TESTU: ZUŻYCIE PROCESORA 2-3%

mtekeli
źródło
1

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 :

try
{
    if (port != null)
        port.Close(); //this will throw an exception if the port was unplugged
}
catch (Exception ex) //of type 'System.IO.IOException'
{
    System.GC.Collect();
    System.GC.WaitForPendingFinalizers();
}

port = null;
Jay Tennant
źródło
0

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.

Chris S.
źródło
0

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.

harry4516
źródło
0

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

Max CHien
źródło
0

Jeśli tworzysz wiele nowych System.Drawing.Bitmapobiektó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”. Dzwonienie GC.Collect()co jakiś czas (niezbyt często!) Wydaje się rozwiązywać ten problem.

Nacht
źródło