Frage zum Garbage Collector in .NET (Speicherleck)

7

Ich denke, das ist sehr einfach, aber da ich selbst .NET lerne, muss ich diese Frage stellen.

Ich bin es gewohnt, in C zu schreiben, wo du free() alles haben musst. In C ++ /. NET habe ich über den Garbage Collector gelesen. Soweit ich weiß, wird eine Instanz, die nicht mehr verwendet wird (im Bereich des Objekts), vom Garbage Collector freigegeben.

Also habe ich eine kleine Testanwendung erstellt. Aber es scheint, dass ich nichts bekommen habe, weil, wenn ich dieselben Dinge ein paar Mal mache (wie, ein Formular öffne, es schließe, es wieder öffne usw.), Speicherlecks. Und große Zeit.

Ich habe versucht, das auf Google nachzuschlagen, aber ich habe nichts gefunden, was gut für einen Anfänger ist.

  1. Gibt der Garbage Collector wirklich alle Objekte frei, wenn sie nicht mehr benutzt werden oder es Ausnahmen gibt, die ich behandeln muss? Was vermisse ich?
  2. Gibt es kostenlose Tools, um nach Speicherlecks zu suchen?
Procule 13.11.2009, 21:52
quelle

7 Antworten

11

Ja, der Garbage Collector befreit Ihre Objekte, wenn sie nicht mehr benutzt werden.

Was wir in .NET normalerweise als Speicherleck bezeichnen, ist eher wie folgt:

  • Sie verwenden externe Ressourcen (die keine Garbage Collection sind) und vergessen, sie zu löschen. Dies wird normalerweise durch die Implementierung der IDisposable-Schnittstelle gelöst.
  • Sie denken, dass es keine Verweise auf ein bestimmtes Objekt gibt, aber tatsächlich gibt es irgendwo, und Sie verwenden sie nicht mehr, aber der Müllsammler weiß nichts über sie.

Außerdem wird Speicher nur bei Bedarf zurückgewonnen, was bedeutet, dass der Garbage Collector zu bestimmten Zeiten aktiviert wird und eine Sammlung ausführt, die bestimmt, welcher Speicher freigegeben und freigegeben werden kann. Bis es so ist, wird der Speicher nicht beansprucht, so dass es aussieht, als wäre Speicher verloren.

Hier, ich denke, ich werde eine komplexere Antwort liefern, nur um zu klären.

Zunächst wird der Garbage Collector in einem eigenen Thread ausgeführt. Sie müssen verstehen, dass der Garbage-Collector alle anderen Threads stoppen muss, damit er die Speicherkette zurückerobern kann, um festzustellen, was davon abhängt. Das ist der Grund, warum Speicher nicht sofort freigegeben wird, ein Müllsammler impliziert bestimmte Kosten in der Leistung.

Intern verwaltet der Garbage Collector Speicher in Generationen. In sehr vereinfachter Weise gibt es mehrere Generationen für langlebige, kurzlebige und große Objekte. Der Garbage Collector verschiebt das Objekt jedes Mal von einer Generation zu einer anderen, wenn es eine Sammlung durchführt, die bei kurzlebigen Generationen häufiger auftritt als bei langlebigen. Es weist auch Objekte neu zu, um Ihnen den größtmöglichen zusammenhängenden Platz zu geben, so dass die Durchführung einer Sammlung ein kostspieliger Prozess ist.

Wenn Sie wirklich sehen möchten, welche Auswirkungen Sie haben, wenn Sie das Formular freigeben (wenn Sie den Geltungsbereich verlassen und keinen Bezug mehr darauf haben), können Sie GC.Collect() aufrufen. Tun Sie das nur zum Testen, es ist sehr unklug, Collect zu nennen, außer in einigen wenigen Fällen, in denen Sie genau wissen, was Sie tun und welche Implikationen es haben wird.

Ein wenig mehr über die Dispose-Methode der IDispose-Schnittstelle.

Dispose ist kein Destruktor in der üblichen C ++ - Art, es ist überhaupt kein Destruktor. Dispose ist eine Möglichkeit, nicht verwaltete Objekte deterministisch zu entfernen . Ein Beispiel: Angenommen, Sie rufen eine externe COM-Bibliothek auf, die zufällig 1 GB Arbeitsspeicher zuweist, aufgrund dessen, was sie tut. Wenn Sie Dispose nicht haben, wird der Speicher dort Platz nehmen, bis der GC eine Auflistung einliest und den nicht verwalteten Speicher durch Aufrufen des eigentlichen Objektdestruktors zurückgewinnt. Wenn Sie die Speicher sofort freigeben möchten, müssen Sie die Dispose-Methode aufrufen, aber Sie sind nicht dazu gezwungen.

Wenn Sie die IDisposable-Schnittstelle nicht verwenden, müssen Sie Ihre nicht verwalteten Ressourcen in der Finalize-Methode freigeben. Finalize wird automatisch vom Objektdestruktor aufgerufen, wenn der GC versucht, das Objekt zurückzufordern. Wenn Sie also eine geeignete Finalize-Methode haben, wird der nicht gemanagte Speicher in beide Richtungen freigegeben. Wenn Sie Dispose aufrufen, wird es nur deterministisch.

    
Jorge Córdoba 13.11.2009, 21:58
quelle
8

Was führt Sie zu dem Schluss, dass es Speicherlecks gibt? Bei der Garbage Collection gibt es keine Garantie dafür, dass der Speicher sofort freigegeben wird und im Allgemeinen tritt der GC nicht in Kraft, bis der Prozessspeicher einen Schwellenwert erreicht oder der verfügbare Heap erschöpft ist. (Die genaue Heuristik ist kompliziert und nicht wichtig.) Die Tatsache, dass der Speicher Ihres Prozesses steigt und nicht untergeht, bedeutet nicht unbedingt, dass es einen Fehler gibt. Es kann nur sein, dass der GC noch nicht dazu gekommen ist, Ihren Prozess zu bereinigen.

Sind Sie darüber hinaus sicher , dass keine Referenzen auf Ihre Objekte vorhanden sind? Es ist möglich, dass Sie Referenzen haben, die Ihnen nicht bekannt sind. Die meisten Speicherverluste in .NET-Anwendungen rühren daher, dass die Leute nicht erkennen, dass auf ihren Speicher immer noch irgendwo verwiesen wird.

    
JSBձոգչ 13.11.2009 21:57
quelle
7

Task-Manager ist eine schreckliche Möglichkeit, Ihre Speichernutzung zu untersuchen. Wenn Sie untersuchen möchten, wie der Garbage Collector funktioniert, installieren Sie den CLR Profiler und verwenden Sie ihn, um Ihre Anwendung zu analysieren. Es wird Ihnen genau sagen, was der Garbage Collector macht.

Siehe Vorgehensweise: Verwenden Sie CLR Profiler .

    
Eric Lippert 14.11.2009 00:25
quelle
4

Ich füge das als Antwort hinzu und nicht als Kommentar zu der Frage, aber dies folgt einer Frage, die vom OP in einem Kommentar gestellt wurde: Verwenden der Anweisung in MSDN. IDisposable Schnittstelle in MSDN.

Hier ist der Kern des Problems: Was Sie bisher von Objektdestruktoren gewohnt sind, ist weg. Alles, was Sie über richtiges Codieren erfahren haben, ist das Aufschreien von Ihrem Unterbewusstsein, das sagt, dass das nicht wahr sein kann, und wenn es wahr ist, ist es falsch und schrecklich. Es ist ganz anders; es ist schwer sich daran zu erinnern, wie sehr ich es zuerst verachtet habe (ich war ein stolzer C ++ Entwickler).

Ich verspreche Ihnen persönlich: Es wird OK!

Hier ist noch eine gute Sache zu lesen: Destruktoren und Finalizer in Visual C ++ .

    
overslacked 13.11.2009 22:23
quelle
3

Der GC wird die Objekte nicht unbedingt in dem Moment freigeben, in dem das Objekt nicht mehr referenziert wird. Der GC wird es irgendwann in der Zukunft sammeln - Sie wissen nicht genau wann, aber wenn Speicher benötigt wird, wird der GC bei Bedarf eine Sammlung durchführen.

    
Michael Burr 13.11.2009 21:55
quelle
1

Wenn Sie nur herausfinden möchten, ob Sie ein Speicherleck haben oder nicht, schauen Sie sich perfmon an welches mit deiner Kopie von Windows geliefert wird:

Insbesondere der .NET CLR-Speicherzähler SOS-Erweiterungen um festzustellen, was undicht ist.

Wenn Sie es sich leisten können, ein paar Dollar auszugeben, werfen Sie einen Blick auf den .NET Memory Profiler .

    
Sam Saffron 14.11.2009 22:55
quelle
0

Es stehen kostenlose Tools zur Verfügung, um den verwalteten Heap in .Net mit den Erweiterungen SOSEX für WinDBG und SOS , es ist möglich, ein Programm auszuführen, es anzuhalten und dann zu sehen, welche Objekte auf dem Stapel vorhanden sind und (was noch wichtiger ist), welche anderen Objekte Referenzen auf sie haben (Roots), die es sein werden verhindert, dass der GC sie sammelt.

    
Colin Desmond 13.11.2009 22:08
quelle

Tags und Links