Im Internet liest man ja immer wieder mal, dass man GC.Collect zwei Mal aufrufen soll (also direkt hintereinander), wenn man sicherstellen will, dass die GarbageCollection wirklich etwas macht. Warum ist das so?
Grundsätzlich rufe nie GC.Collect() auf! Der GarbageCollector wird schon selbst aufräumen, wenn er es für nötig hält. Such eher das Problem warum ein Objekt nicht freigegeben wird. Hast du evtl. vergessen abonnierte Events freizugeben?
Es gibt ein paar wenige Stellen wo man "GC.Collect()" explizit aufrufen kann, will oder muss. Beispielsweise wenn man Objekte mit hunderten Megabytes oder Gigabytes freigibt und den Speicher direkt wieder belegen will. Dann ist es sinnvoll "GC.Collect()" direkt beim Freigeben aufzurufen so das der GarbageCollector bereits im Hintergrund alles freigeben kann wärend ich alles vorbereite um den Speichern neu vollzumüllen :D
Für normal sollte der GC das selbst entscheiden wann er aktiv wird. Der von Floyd gebrachte Fall soll nur dann verwendet werden wenn Profiling zeigt dass es die Leistung (wie die auch immer für den Kontext definiert sein mag) auch tatsächlich verbessert wird. Bei manuellem Aufruf von GC.Collect wird nämlich die gesamte Strategie des GC über den Haufen geworfen und das kann sich auch sehr negativ auswirken.
Ich unterstüze Konstantins Meinung, dass man den GarbageCollector seine Arbeit alleine machen lassen sollte. Etwaige Einmischung riecht förmlich danach, dass man irgendwo im Design oder im Algorithmus ein Problem hat. Ich musste bis jetzt nie solche Hack's anwenden.
Es gibt keine Garantie, dass was freigegeben wird. Der Collector wird es "nur" versuchen. Zitat: "Use this method to force the system to try to reclaim the maximum amount of available memory." Bei java ist es ähnlich, Zitat: "Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse." Ich verstehe das so: Wenn ich weiss, ich habe viele Objekte die freigegeben werden können, kann ich collect() aufrufen und hoffen, der Garbage Collector macht sich an die Arbeit.
Wenn ich weiß, ich habe viele Objekte dann soll sich der GC darum kümmern wann was und wie freigegeben wird solange ich die Objekte nicht benötige. Dafür ist er ja da! Von draußen reinpfuschen mag er gar nicht, denn dann weiß er nicht mehr wo er gerade war. Zitat GC: "Lasst mich einfach in Ruhe! Ich weiß schon was ich tue" (cool das reimt sich sogar ;-)
GC.Collect braucht für normale Anwendung nicht explizti aufgerufen werden!
GC.Collect zweimal aufzurufen um "sicher zu gehen das das Objekt entfernt wird" rührt daher das Objekte mit einem Finalizer 2 durchgänge der GC braucht um entfernt zu werden.
Trotzdem mein rat Finger weg denn bei allen Objekten die nicht entfernt werden, wird die Generation erhöht.
Die GC entfernt aber vorzugsweise Objekte aus einer niedrigen Generation so das du hiermit eher das gegenteil erreichst weil sich deine Objekte durch die hohe Generation im speicher "festsetzten".
Ich hoffe das ich dir beim Verständnis des Problems weiterhelfen konnte. Gruß Thomas
PS. du müsstest jetzt eigentlich auch als Antwort markieren können ;-)
Der Rat mit dem zweimaligen Aufruf kommt vermutlich daher, dass Objekte mit Finalizer zwei Durchläufe des GC brauchen, bis sie wirklich aufgeräumt sind. Im ersten Durchlauf stellt der GC fest, dass sie nicht mehr referenziert werden, und stellt sie in die Finalisierungs-Queue. Irgendwann später widmet sich der GC dann mal dieser Queue, ruft auf den Objekten in ihr die Finalizer auf, und erst danach wird in einer weiteren Garbage Collection der Speicher dieser Objekte wirklich freigegeben.
Wobei ich mir nicht sicher bin, ob das zweimalige Aufrufen von GC.Collect() (mal ganz abgesehen von den Vorbehalten, die Konstantin vorgebracht hat) direkt hintereinander da wirklich Abhilfe schafft, denn das Timing der Finalizer ist ziemlich vage definiert - sie werden "irgendwann" aufgerufen.
Dieses Verhalten ist übrigens auch ein Grund für das Standard-Pattern zur Implementierung von Finalizer und IDisposable (beschrieben in der MSDN). Die Cleanup-Aufgaben, die spätestens im Finalizer erledigt werden müssen, können und sollen natürlich auch schon beim Aufruf von Dispose() erledigt werden. Dann aber muss der Finalizer nicht auch noch laufen, weshalb man in Dispose() am Ende GC.SuppressFinalize(this) ruft und damit erreicht, dass das Objekt schon beim ersten GC-Durchlauf freigegeben wird.