webentwicklung-frage-antwort-db.com.de

Wann kann ich GC.Collect anrufen?

Der generelle Ratschlag lautet, dass Sie GC.Collect nicht aus Ihrem Code aufrufen sollten, aber was sind die Ausnahmen von dieser Regel?

Ich kann nur an einige sehr spezielle Fälle denken, in denen es sinnvoll sein kann, eine Müllsammlung zu erzwingen. 

Ein Beispiel, das mir in den Sinn kommt, ist ein Service, der in regelmäßigen Abständen aufwacht, Aufgaben ausführt und dann lange schläft. In diesem Fall ist es möglicherweise eine gute Idee, ein Sammeln zu erzwingen, um zu verhindern, dass der bald auslaufende Prozess mehr Speicher als benötigt hält. 

Gibt es andere Fälle, in denen der Aufruf von GC.Collect zulässig ist?

151
Brian Rasmussen

Wenn Sie Grund zu der Annahme haben, dass eine bedeutende Anzahl von Objekten - insbesondere die, von denen Sie vermuten, dass sie in den Generationen 1 und 2 sind - jetzt für die Müllsammlung in Frage kommen, und dass dies nun eine angemessene Zeit für die Erfassung der geringen Leistung ist? .

Ein gutes Beispiel dafür ist, wenn Sie gerade ein großes Formular geschlossen haben. Sie wissen, dass alle Steuerelemente der Benutzeroberfläche jetzt Müll gesammelt werden können, und eine sehr kurze Pause, da das Formular geschlossen wird, wird dem Benutzer wahrscheinlich nicht auffallen.

UPDATE 2.7.2018

Ab .NET 4.5 gibt es GCLatencyMode.LowLatency und GCLatencyMode.SustainedLowLatency. Beim Aufrufen und Verlassen eines dieser Modi wird empfohlen, einen vollständigen GC mit GC.Collect(2, GCCollectionMode.Forced) zu erzwingen.

Ab .NET 4.6 gibt es die Methode GC.TryStartNoGCRegion (zum Festlegen des schreibgeschützten Wertes GCLatencyMode.NoGCRegion). Dies kann selbst eine vollständige blockierende Speicherbereinigung durchführen, um genügend Speicherplatz freizugeben, aber da wir GC für einen bestimmten Zeitraum nicht zulassen, würde ich behaupten, dass es auch eine gute Idee ist, vorher und nachher eine vollständige GC durchzuführen.

Quelle: Microsoft-Ingenieur Ben Watson: Hochleistungs-.NET-Code schreiben, 2. Aufl. 2018.

Sehen:

142
Jon Skeet

Ich verwende GC.Collect nur beim Schreiben von groben Performance/Profiler-Testgeräten. Das heißt, ich habe zwei (oder mehr) Codeblöcke zum Testen - etwa:

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

Damit funktionieren TestA() und TestB() mit einem möglichst ähnlichen Zustand - d. H. TestB() wird nicht gehämmert, nur weil TestA sich sehr nahe am Kipppunkt befindet.

Ein klassisches Beispiel wäre ein einfaches Konsolenexe (eine Main-Methode, die zum Beispiel hier veröffentlicht wurde), die den Unterschied zwischen der Verkettung von Schleifenzeichenfolgen und StringBuilder darstellt.

Wenn ich etwas Genaues brauche, wären das zwei völlig unabhängige Tests - aber oft genug, reicht es aus, wenn wir die GC während der Tests minimieren (oder normalisieren) wollen, um ein grobes Gefühl für das Verhalten zu bekommen.

Während der produktion code? Ich muss es noch verwenden ;-p

47
Marc Gravell

In den meisten Fällen empfiehlt es sich, keine Garbage Collection zu erzwingen. (Jedes System, an dem ich gearbeitet habe und das Garbage Collections erzwungen hat, hat Probleme unterstrichen, wenn gelöst hätte die Notwendigkeit, die Speicherbereinigung zu erzwingen, beseitigt und das System erheblich beschleunigt.)

Es gibt wenige Fälle , in denen Sie mehr über die Speichernutzung wissen als die Müllsammler tut. Es ist unwahrscheinlich, dass dies in einer Mehrbenutzeranwendung oder einem Dienst, der auf mehrere Anforderungen gleichzeitig reagiert, der Fall ist.

In einigen Batch-Typ-Verarbeitungen kennen Sie jedoch mehr als den GC. Z.B. Betrachten Sie eine Anwendung, die.

  • Erhält eine Liste mit Dateinamen in der Befehlszeile
  • Verarbeitet eine einzelne Datei und schreibt das Ergebnis in eine Ergebnisdatei.
  • Erstellt während der Verarbeitung der Datei viele verknüpfte Objekte, die erst erfasst werden können, wenn die Verarbeitung der Datei abgeschlossen ist (z. B. ein Analysebaum).
  • Der Übereinstimmungsstatus zwischen den verarbeiteten Dateien wird nicht beibehalten .

Möglicherweise können Sie (nach sorgfältigen) Tests feststellen, dass Sie eine vollständige Garbage Collection erzwingen sollten, nachdem Sie jede Datei verarbeitet haben.

Ein anderer Fall ist ein Dienst, der alle paar Minuten zur Verarbeitung einiger Elemente aufwacht und keinen Status beibehält, während er schläft . Dann kann es sich lohnen, eine vollständige Sammlung kurz vor dem Einschlafen zu erzwingen .

Ich würde nur in Betracht ziehen, eine Sammlung zu erzwingen, wenn ich weiß, dass in letzter Zeit viele Objekte erstellt wurden und derzeit nur sehr wenige Objekte referenziert werden.

Ich hätte lieber eine Garbage Collection-API, wenn ich Hinweise auf diese Art von Dingen geben könnte, ohne dass ich einen GC erzwingen müsste.

Siehe auch " Rico Marianis Performance-Leckerbissen "

29
Ian Ringrose

Ein Fall ist der Fall, wenn Sie versuchen, Testcode zu erstellen, der WeakReference verwendet.

18
Brian

In großen 24/7 oder 24/6 Systemen - Systemen, die auf Nachrichten, RPC-Anforderungen reagieren oder eine Datenbank oder einen Prozess kontinuierlich abfragen - ist es hilfreich, Speicherlecks zu identifizieren. Ich neige dazu, der Anwendung einen Mechanismus hinzuzufügen, um die Verarbeitung vorübergehend auszusetzen und dann eine vollständige Speicherbereinigung durchzuführen. Dadurch wird das System in einen Ruhezustand versetzt, in dem der verbleibende Speicher entweder legitim langlebiger Speicher ist (Caches, Konfiguration usw.) oder "durchgesickert" ist (Objekte, die nicht erwartet werden oder verwurzelt werden sollen, aber tatsächlich sind).

Durch diesen Mechanismus ist es viel einfacher, die Speicherverwendung zu profilieren, da die Berichte nicht durch das Rauschen der aktiven Verarbeitung getrübt werden.

Um sicherzustellen, dass Sie den gesamten Müll erhalten, müssen Sie zwei Sammlungen durchführen:

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

Bei der ersten Sammlung werden alle Objekte mit Finalizern finalisiert (die Objekte werden jedoch nicht tatsächlich von Garbage gesammelt). Der zweite GC sammelt diese finalisierten Objekte.

9
Paul Ruane

Sie können GC.Collect () aufrufen, wenn Sie etwas über die Art der App wissen, die der Garbage Collector nicht hat. Es ist verlockend zu glauben, dass dies als Autor sehr wahrscheinlich ist. Die Wahrheit ist jedoch, dass der GC ein ziemlich gut geschriebenes und erprobtes Expertensystem darstellt, und es ist selten, dass Sie etwas über die einfachen Codepfade wissen, die es nicht gibt.

Das beste Beispiel, an dem Sie möglicherweise zusätzliche Informationen erhalten, ist eine App, die zwischen Leerlaufperioden und sehr belebten Perioden wechselt. Sie möchten die bestmögliche Leistung für die anstrengenden Perioden und möchten daher die Leerlaufzeit verwenden, um einige Aufräumarbeiten durchzuführen. 

Meistens ist der GC jedoch klug genug, dies trotzdem zu tun.

8
Joel Coehoorn

Schauen Sie sich diesen Artikel von Rico Mariani an. Er gibt zwei Regeln für den Aufruf von GC.Collect an (Regel 1 lautet: "Nicht"):

Wann rufen Sie GC.Collect () auf

7
M4N

Als Speicherfragmentierungslösung. Ich habe die Speicherausnahmen verlassen, während ich viele Daten in einen Speicherstrom geschrieben habe (aus einem Netzwerkstrom gelesen). Die Daten wurden in 8-KByte-Brocken geschrieben. Nachdem 128M erreicht waren, gab es eine Ausnahme, obwohl viel Speicher verfügbar war (der Speicher war jedoch fragmentiert). Beim Aufruf von GC.Collect () wurde das Problem behoben. Ich konnte nach dem Fix mehr als 1G verarbeiten.

7
Ethos

Ich habe einige Leistungstests für Array und Liste durchgeführt:

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));
}

und ich bekam OutOfMemoryException in der GetSomeNumbers_Enumerable_Range-Methode. Die einzige Problemumgehung besteht darin, den Speicher aufzulösen, indem:

numbers = null;
GC.Collect();
5
Daniel B

In Ihrem Beispiel denke ich, dass das Aufrufen von GC.Collect nicht das Problem ist, sondern eher ein Designproblem.

Wenn Sie in bestimmten Abständen aufwachen (eingestellte Zeiten), sollte Ihr Programm für eine einzelne Ausführung erstellt werden (führen Sie die Aufgabe einmal durch) und beenden Sie dann. Anschließend richten Sie das Programm als geplante Task ein, die in den geplanten Intervallen ausgeführt wird.

Auf diese Weise müssen Sie sich nicht mit dem Aufruf von GC.Collect beschäftigen (was Sie selten tun sollten, wenn überhaupt).

Rico Mariani hat zu diesem Thema einen großartigen Blogpost, den Sie hier finden können:

http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx

4
casperOne

Eine Instanz, in der es fast notwendig ist, GC.Collect () aufzurufen, ist die Automatisierung von Microsoft Office über Interop. COM-Objekte für Office möchten nicht automatisch freigegeben werden. Dies kann dazu führen, dass das Office-Produkt sehr viel Speicherplatz beansprucht. Ich bin mir nicht sicher, ob es sich hierbei um ein Problem oder um ein Design handelt. Es gibt viele Beiträge zu diesem Thema im Internet, daher werde ich nicht zu sehr ins Detail gehen.

Bei der Programmierung mit Interop sollte jedes einzelne COM-Objekt manuell freigegeben werden, normalerweise durch Verwendung von Marshal.ReleseComObject (). Wenn Sie die Garbage Collection manuell aufrufen, kann dies ein wenig "bereinigen". Wenn Sie mit Interop-Objekten fertig sind, scheint der folgende Code aufzurufen:

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

Nach meiner persönlichen Erfahrung verringert die Verwendung einer Kombination aus ReleaseComObject und das manuelle Aufrufen der Garbage Collection stark den Speicherbedarf von Office-Produkten, insbesondere von Excel.

3
garthhh

Eine nützliche Stelle zum Aufrufen von GC.Collect () ist ein Komponententest, wenn Sie sicherstellen möchten, dass kein Speicherverlust erstellt wird (z. B. wenn Sie WeakReferences oder ConditionalWeakTable verwenden, dynamisch generierten Code usw.).

Zum Beispiel habe ich einige Tests wie:

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

Es könnte argumentiert werden, dass die Verwendung von WeakReferences an und für sich ein Problem darstellt. Wenn Sie jedoch ein System erstellen, das auf einem solchen Verhalten beruht, ist das Aufrufen von GC.Collect () eine gute Möglichkeit, solchen Code zu überprüfen.

3
ChaseMedallion

ich bin immer noch ziemlich unsicher darüber ... Ich arbeite seit 7 Jahren an einem Application Server. Bei größeren Installationen werden 24 GB RAM verwendet. Seine hochgradigen Multithreading- und ALL-Aufrufe für GC.Collect () hatten wirklich schreckliche Leistungsprobleme.

Viele Komponenten von Drittanbietern verwendeten GC.Collect (), als sie es für klug hielten, dies jetzt zu tun ... Ein einfacher Haufen Excel-Reports blockierte den App Server mehrmals pro Minute für alle Threads.

Wir mussten alle Komponenten von Drittanbietern umgestalten, um die Aufrufe von GC.Collect () zu entfernen, und danach funktionierten alle einwandfrei.

Aber ich verwende auch Server unter Win32, und hier begann ich, GC.Collect () intensiv zu nutzen, nachdem ich eine OutOfMemoryException erhalten hatte.

Aber ich bin darüber auch ziemlich unsicher, denn ich habe oft gemerkt, dass ich ein OOM auf 32 Bit bekomme und die gleiche Operation erneut ausführen möchte, ohne GC.Collect () aufzurufen.

Eine Sache, die ich frage mich, ist die OOM-Ausnahme selbst ..... Wenn ich das .NET Framework geschrieben hätte und keinen Speicherblock zuordnen kann, würde ich GC.Collect () verwenden, den Speicher defragmentieren (??). , versuche es erneut, und wenn ich immer noch keinen freien Speicherblock finden kann, würde ich die OOM-Exception werfen.

Oder Sie sollten dieses Verhalten zumindest aufgrund der Nachteile des Leistungsproblems mit GC.Collect als konfigurierbare Option festlegen.

Jetzt habe ich viel Code wie diesen in meiner App, um das Problem zu "lösen":

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);
}

(Beachten Sie, dass das Verhalten von Thread.Sleep () wirklich ein App-spezifisches Verhalten ist, da wir einen ORM-Caching-Dienst ausführen und der Dienst einige Zeit benötigt, um alle zwischengespeicherten Objekte freizugeben, wenn RAM einige vordefinierte Werte überschreitet. es wartet also beim ersten Mal ein paar Sekunden und hat die Wartezeit bei jedem Auftreten von OOM erhöht.)

2
Thomas Haller
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();
   //
}
2
Denis

Es gibt Situationen, in denen es sicherer ist als Entschuldigung. 

Hier ist eine Situation. 

Es ist möglich, ein unmanaged DLL in C # zu erstellen, indem IL-Überschreibungen verwendet werden (da es Situationen gibt, in denen dies erforderlich ist). 

Angenommen, die DLL erstellt beispielsweise ein Array von Bytes auf Klassenebene - da viele der exportierten Funktionen auf solche zugreifen müssen. Was passiert, wenn die DLL entladen wird? Wird der Garbage Collector an diesem Punkt automatisch aufgerufen? Ich weiß es nicht, aber als unmanaged DLL ist es durchaus möglich, dass der GC nicht aufgerufen wird. Und es wäre ein großes Problem, wenn es nicht angerufen würde. Wenn die DLL entladen wird, ist dies auch der Garbage-Collector. Wer wird also für das Sammeln möglicher Abfälle verantwortlich sein und wie würden sie das tun? Besser, den Müllsammler von C # einzusetzen. Verfügen über eine Bereinigungsfunktion (für den DLL - Client verfügbar), bei der die Variablen auf Klassenebene auf null gesetzt und der Garbage Collector aufgerufen wird.

Sicher ist sicher.

2
Carl Looper

Scott Holdens Blogeintrag, wann (und wann nicht) GC.Collect aufgerufen wird, ist spezifisch für das .NET Compact Framework . Die Regeln gelten jedoch allgemein für alle verwalteten Entwicklungen.

2
ctacke

Die kurze Antwort lautet: Niemals!

2
BankZ

Sie sollten versuchen, GC.Collect () zu vermeiden, da es sehr teuer ist. Hier ist ein Beispiel:

        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
    }

TESTERGEBNIS: CPU VERWENDUNG 12%

Wenn Sie dies ändern:

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

TESTERGEBNIS: CPU VERWENDUNG 2-3%

1
mtekeli

Für die Frage ist das nicht so wichtig, aber für XSLT-Transformationen in .NET (XSLCompiledTranform) haben Sie möglicherweise keine Wahl. Ein weiterer Kandidat ist das MSHTML-Steuerelement.

0
Chris S

Da gibt es Small Object Heap (SOH) und Large Object Heap (LOH)

Wir können GC.Collect () aufrufen, um das Referenzobjekt in SOP zu löschen und das gelebte Objekt zur nächsten Generation zu verschieben.

In .net4.5 können Sie LOH auch komprimieren, indem Sie largeobjectheapcompactionmode verwenden. 

0
MaxJ

ein guter Grund für das Aufrufen von GC sind kleine ARM - Computer mit wenig Arbeitsspeicher, wie der Raspberry Pi (läuft mit Mono). Wenn nicht zugewiesene Speicherfragmente zu viel Arbeitsspeicher beanspruchen, kann das Linux-Betriebssystem dies erreichen unstable . Ich habe eine Anwendung, bei der ich GC (!) jede Sekunde anrufen muss, um Speicherüberlaufprobleme zu beseitigen.

Eine andere gute Lösung besteht darin, Objekte zu entsorgen, wenn sie nicht mehr benötigt werden. Leider ist das in vielen Fällen nicht so einfach.

0
harry4516

Wenn Sie eine .net-Version unter 4.5 verwenden, ist die manuelle Erfassung möglicherweise unvermeidlich (insbesondere, wenn Sie mit vielen "großen Objekten" arbeiten). 

dieser link beschreibt warum:

https://blogs.msdn.Microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/

0
fusi