C# Verständnisfrage zum Garbage Collector (in Verbindung mit Listen aus Objekten)

Phoenixz

Lieutenant
Registriert
März 2004
Beiträge
595
Hallo,

ich habe eine Verständnisfrage zum Garbage Collector. Warum wird in diesem, zugegebenermaßen künstlich erzeugten Beispiel der Speicher nicht wieder freigegeben?

Code:
        private void button1_Click(object sender, EventArgs e)
        {
            HashSet<Test> tests = new HashSet<Test>();

            for (int i = 1; i < 6000; i++) // 6k Einträge erstellen
            {
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < 1000; j++) // große Einträge erstellen
                {
                    sb.Append("sdfkjasdhfkashd fklajshdf,kjsahdlfkjsahdlfkjhas ldkfjhsaldkf haslkdfbasljkfgbvlahsbclkavbl euvczlsaj,bdhvl,");
                }
                tests.Add(new Test() { Text = sb.ToString() });
            }

            tests.Clear();
            tests = null;
        }

Code:
    class Test
    {
        public string Text { get; set; }
    }

Wo der erste Code-Block steht ist eigentlich egal. Ich habe ihn einfach mal in den EventHandler eines Buttons einer Windows Forms Anwendung gepackt.

Man kann das ja quasi runterbrechen und behaupten, dass wann immer man mit einer Liste aus Objekten innerhalb einer Methode arbeitet und diese eigentlich nur für interne Zwecke der Methode braucht, wird der Speicher nicht mehr freigegeben?

Generell heißt es eigentlich, dass man den Aufruf von GC.Collect() vermeiden sollte.
Führe ich allerdings
Code:
            ...
            tests.Clear();
            tests = null;
            GC.Collect();
        }
aus, ist der Speicher wieder freigegeben.

Aus einem rein theoretischem Interesse würde ich gerne wissen warum der GC den Speicher nicht von alleine freigibt und wie man, wenn eine solche Liste eine große Menge an Objekten enthält (+1GB) diese am besten wieder freigibt?

Vielen Dank,
Daniel

PS: Es ist übrigens egal ob HashSet oder List.
 
Der Speicher wird ja in dem beschriebenen Fall wieder automatisch freigegeben, nur eben nicht sofort.

Die Gefahr, wenn man die GV forciert ist eben, dass die Programmausführung dann kurz stockt, weil sich das System nicht mehr mit der Programmausführung beschäftigt, sondern mit der GC.
 
Um es mal zu verklausurieren: Garbage Collector Aufrufe sind zeitaufwendig und blockierend. Es gibt diverse Strategien, wann und wie oft ein GC aufgerufen werden soll. Eine davon ist, erst den GC zu verwenden, wenn der Speicher knapp wird. Oder, in einem bestimmten Zeitabstand.

Deswegen wird in deinem Beispiel die Liste nicht unmittelbar aufgeräumt.
 
Der Speicher wird ja in dem beschriebenen Fall wieder automatisch freigegeben, nur eben nicht sofort.

Was meinst du damit konkret? Im Kontext einer Windows Forms Anwendung und eben den Code im EventHandler eines Buttons kann man zeitlich so lange warten wie man will, der Speicher wird nicht mehr freigegeben. Auch kann man andere Buttons einbauen und Ausführen trotzdem wird er nicht freigegeben. Das der Speicher in der Theorie nicht sofort aufgeräumt wird ist mir auch klar. Jedoch habe ich es ja nichtmal geschafft, dass er irgendwann aufgeräumt wird.

Eine davon ist, erst den GC zu verwenden, wenn der Speicher knapp wird.
Ahh, ok, danke. Das würde dann Sinn ergeben, warum beim zweiten klick auf den Button der Speicher wieder freigegeben wird, aber bei allen anderen Buttons nicht. Danke!
Gibt es, bis auf den zeitlichen Aspekt noch weitere Beweggründe GC.Collect() nicht aufzurufen?
 
Zuletzt bearbeitet:
Phoenixz schrieb:
Was meinst du damit konkret? Im Kontext einer Windows Forms Anwendung und eben den Code im EventHandler eines Buttons kann man zeitlich so lange warten wie man will, der Speicher wird nicht mehr freigegeben. Auch kann man andere Buttons einbauen und Ausführen trotzdem wird er nicht freigegeben. Das der Speicher in der Theorie nicht sofort aufgeräumt wird ist mir auch klar. Jedoch habe ich es ja nichtmal geschafft, dass er irgendwann aufgeräumt wird.
Da der GC nicht deterministisch arbeitet, kann dir niemand sagen wann der Speicher genau freigeben wird. Es ist also völlig egal wie lange du wartest, weil es immer ein wenig zu kurz sein könnte.

Phoenixz schrieb:
Gibt es, bis auf den zeitlichen Aspekt noch weitere Beweggründe GC.Collect() nicht aufzurufen?
GC.Collect() hebelt im Grunde das Konzept "Managed Code" aus. Der GC ist dazu da, dass du dich nicht darum kümmern musst Speicher freizugeben. Das manuelle Anstoßen existiert nur für Sonderfälle.
Dein Beispiel ist zudem extrem und tritt in der Praxis so geballt nicht auf. Dass hier und da mal ein paar "normalgroße" Objekte noch nicht freigeben sind fällt niemanden auf.
 
Wenn du mehr Interesse hast, dann les dich mal ein zum Large Object Heap, auf dem landen nämlich Objekte, die größer als 85k Bytes sind.
In der Regel tritt das bereits bei rund 1000 Double Werten ein.
Außerdem verhält sich die Garbage Collection dort ein bisschen anders...sie schaut dort nämlich nicht so oft vorbei.

Generell ist die GC aber wohl so unberechenbar bzw. durchschaubar wie eine Frau ;-)

Und der manuelle Aufruf von ihr macht sie nur wütend und das lässt sie dich in der Regel spüren...
 
Zuletzt bearbeitet:
Phoenixz schrieb:
Jedoch habe ich es ja nichtmal geschafft, dass er irgendwann aufgeräumt wird.

Grundsatzfrage: Wie stellst du eigentlich fest, welcher Speicher echt verfügbar und welcher echt belegt ist? Weiß ich jetzt ohne weiteres auch nicht, wie man das abfragt.

Gibt es da bei C# Metainformationen, wo man das ausgeben lassen kann?

Es sollte schon klar sein, dass nicht der Speicher, der vom Betriebssystem angefordert wird belegt ist, sondern nur möglicherweise belegt ist.

So weit ich das vermute, ist GarbageCollection eben nur eine Art von Reference Counting, und der Garbage Collector betrachtet immer alle Objekte und stellt fest, wenn sie verwaist sind und löscht sie dann. Nur arbeitet das Teil eben mit "To-Do-Listen", die immer wieder mal abgearbeitet werden, daher können eben verwaiste Objekte noch eine Zeit lang leben und so Platz belegen, obwohl sie eigentlich schon tot sind.
 
Zuletzt bearbeitet:
Alles klar, vielen Dank für alle Antworten! Damit ist meine Frage definitiv beantwortet :).
 

Ähnliche Themen

Zurück
Oben