Frage

Welche Möglichkeiten gibt es, um Speicherlecks in .NET zu bekommen?

Ich kenne zwei:

  1. Nicht ordnungsgemäße Abmeldung Event-Handler/Delegierte.
  2. Dynamische untergeordnete Steuerelemente in Windows Forms nicht entsorgen:

Beispiel:

// Causes Leaks  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// Correct Code  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

Aktualisieren:Die Idee besteht darin, häufige Fallstricke aufzulisten, die nicht allzu offensichtlich sind (wie die oben genannten).Normalerweise geht man davon aus, dass Speicherlecks aufgrund des Garbage Collectors kein großes Problem darstellen.Nicht mehr wie früher in C++.


Tolle Diskussion, Leute, aber lassen Sie mich das klarstellen ...Per Definition gilt: Wenn in .NET kein Verweis mehr auf ein Objekt vorhanden ist, wird es irgendwann in der Garbage Collection erfasst.Das ist also keine Möglichkeit, Speicherlecks hervorzurufen.

In der verwalteten Umgebung würde ich es als Speicherverlust betrachten, wenn Sie einen unbeabsichtigten Verweis auf ein Objekt hätten, das Ihnen nicht bekannt ist (daher die beiden Beispiele in meiner Frage).

Auf welche verschiedenen Arten kann es also zu einem solchen Speicherverlust kommen?

War es hilfreich?

Lösung

Blockieren Sie den Finalizer-Thread.Bis die Blockierung des Finalizer-Threads aufgehoben wird, werden keine anderen Objekte durch Garbage Collection erfasst.Somit wird die Menge des verwendeten Speichers immer größer.

Weiterführende Literatur: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

Andere Tipps

Das führt nicht wirklich zu Lecks, es macht dem GC nur mehr Arbeit:

// slows GC
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// better  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

// best
using( Label label = new Label() )
{ 
    this.Controls.Add(label);  
    this.Controls.Remove(label);  
}

In einer verwalteten Umgebung wie .Net ist es nie ein großes Problem, Einwegkomponenten auf diese Weise herumliegen zu lassen – das ist ein großer Teil dessen, was verwaltet bedeutet.

Sie werden Ihre App auf jeden Fall verlangsamen.Aber Sie werden für nichts anderes ein Chaos hinterlassen.

Einstellen der GridControl.DataSource Eigenschaft direkt ohne Verwendung einer Instanz der BindingSource-Klasse (http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx).

Dies führte zu Lecks in meiner Anwendung, die ich mit einem Profiler ziemlich lange aufspüren musste. Schließlich fand ich diesen Fehlerbericht, auf den Microsoft reagierte: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

Es ist lustig, dass Microsoft in der Dokumentation für die BindingSource-Klasse versucht, sie als legitime, gut durchdachte Klasse auszugeben, aber ich denke, sie haben sie nur erstellt, um ein grundlegendes Leck in Bezug auf Währungsmanager und die Bindung von Daten an Rastersteuerungen zu beheben.

Achten Sie darauf, ich wette, dass es aus diesem Grund jede Menge undichte Anwendungen gibt!

Es gibt keine Möglichkeit, eine umfassende Liste bereitzustellen ...Das ist so, als würde man fragen: „Wie kann man nass werden?“

Stellen Sie jedoch sicher, dass Sie Dispose() für alles aufrufen, was IDisposable implementiert, und stellen Sie sicher, dass Sie IDisposable für alle Typen implementieren, die nicht verwaltete Ressourcen jeglicher Art verbrauchen.

Führen Sie hin und wieder etwas wie FxCop auf Ihrer Codebasis aus, um diese Regel durchzusetzen – Sie werden überrascht sein, wie tief einige verfügbare Objekte in einem Anwendungsframework vergraben sind.

Ausnahmen in Finalize-Methoden (oder Dispose-Aufrufen von einem Finalizer), die verhindern, dass nicht verwaltete Ressourcen ordnungsgemäß entsorgt werden.Ein häufiger Fehler liegt am Programmierer vorausgesetzt In welcher Reihenfolge werden Objekte entsorgt, und der Versuch, bereits entsorgte Peer-Objekte freizugeben, führt zu einer Ausnahme und der Rest der Finalise/Dispose from Finalize-Methode wird nicht aufgerufen.

Ich möchte dieser Diskussion noch vier weitere Punkte hinzufügen:

  1. Das Beenden von Threads (Thread.Abort()), die UI-Steuerelemente erstellt haben, ohne sich ordnungsgemäß auf ein solches Ereignis vorzubereiten, kann dazu führen, dass der Speicher erwartungsgemäß verwendet wird.

  2. Der Zugriff auf nicht verwaltete Ressourcen über Pinvoke und das Nichtbereinigen dieser Ressourcen kann zu Speicherverlusten führen.

  3. Ändern großer String-Objekte.Es handelt sich nicht unbedingt um einen Speicherverlust, sobald er außerhalb des Gültigkeitsbereichs liegt, wird sich GC darum kümmern. Allerdings kann es in Bezug auf die Leistung zu Einbußen bei Ihrem System kommen, wenn große Zeichenfolgen häufig geändert werden, da Sie sich nicht wirklich darauf verlassen können, dass GC den Platzbedarf Ihres Programms gewährleistet minimal.

  4. Erstellen Sie häufig GDI-Objekte, um benutzerdefinierte Zeichnungen durchzuführen.Wenn Sie häufig GDI-Arbeiten durchführen, verwenden Sie ein einzelnes GDI-Objekt wieder.

Das Aufrufen von IDisposable jedes Mal ist der einfachste Ausgangspunkt und definitiv eine effektive Möglichkeit, alle niedrig hängenden Speicherverluste in der Codebasis zu erfassen.Allerdings reicht es nicht immer aus.Beispielsweise ist es auch wichtig zu verstehen, wie und wann verwalteter Code zur Laufzeit generiert wird und dass Assemblys, sobald sie in die Anwendungsdomäne geladen sind, nie wieder entladen werden, was den Anwendungsbedarf vergrößern kann.

So verhindern Sie .NET-Speicherlecks:

1) Verwenden Sie das Konstrukt „using“ (oder das Konstrukt „try-finally“), wann immer ein Objekt mit der Schnittstelle „IDisposable“ erstellt wird.

2) Machen Sie Klassen zu „IDisposable“, wenn sie einen Thread erstellen oder ein Objekt zu einer statischen oder langlebigen Sammlung hinzufügen.Denken Sie daran, dass ein C#-„Ereignis“ eine Sammlung ist.

Hier ist ein kurzer Artikel zum Thema Tipps zur Vermeidung von Speicherlecks.

Sprechen Sie über unerwartete Speichernutzung oder tatsächliche Lecks?Bei den beiden Fällen, die Sie aufgelistet haben, handelt es sich nicht gerade um Leaks;Dabei handelt es sich um Fälle, in denen Gegenstände länger als vorgesehen haften bleiben.

Mit anderen Worten handelt es sich um Referenzen, von denen die Person, die sie als „Memory Leaks“ bezeichnet, nichts wusste oder von denen sie sie vergessen hatte.

Bearbeiten:Oder es handelt sich um tatsächliche Fehler im Garbage Collector oder um nicht verwalteten Code.

Bearbeiten 2:Eine andere Möglichkeit, darüber nachzudenken, besteht darin, immer sicherzustellen, dass externe Verweise auf Ihre Objekte ordnungsgemäß freigegeben werden.„Extern“ bedeutet Code, der außerhalb Ihrer Kontrolle liegt.In jedem Fall, in dem dies geschieht, kann es zu einem Speicherverlust kommen.

  1. Bewahren Sie Verweise auf Objekte auf, die Sie nicht mehr benötigen.

Zu anderen Kommentaren: Eine Möglichkeit, sicherzustellen, dass Dispose aufgerufen wird, ist die Verwendung von ...wenn die Codestruktur es zulässt.

Eine Sache, die für mich wirklich unerwartet war, ist Folgendes:

Region oldClip = graphics.Clip;
using (Region newClip = new Region(...))
{
    graphics.Clip = newClip;
    // draw something
    graphics.Clip = oldClip;
}

Wo ist das Speicherleck?Richtig, du hättest entsorgen sollen oldClip, zu!Weil Graphics.Clip ist eine der seltenen Eigenschaften, die bei jedem Aufruf des Getters ein neues verfügbares Objekt zurückgibt.

Tess Fernandez hat tolle Blogbeiträge zum Finden und Debuggen von Speicherlecks.Labor 6 Labor 7

Viele Dinge, die in nicht verwalteten Sprachen zu Speicherverlusten führen können, können auch in verwalteten Sprachen zu Speicherverlusten führen.Zum Beispiel, schlechte Caching-Richtlinien kann zu Speicherverlusten führen.

Aber wie Greg und Danny gesagt haben, gibt es keine umfassende Liste.Alles, was dazu führen kann, dass der Speicher nach Ablauf seiner Nutzungsdauer blockiert wird, kann ein Leck verursachen.

Blockierte Threads geben niemals Roots frei.Natürlich könnte man argumentieren, dass der Deadlock ein größeres Problem darstellt.

Ein blockierter Finalizer-Thread verhindert die Ausführung aller verbleibenden Finalizer und verhindert somit, dass alle finalisierbaren Objekte zurückgefordert werden (da sie immer noch von der freachable-Liste gerootet werden).

Auf einem Computer mit mehreren CPUs könnten Sie finalisierbare Objekte schneller erstellen, als der Finalizer-Thread Finalizer ausführen könnte.Solange dies anhält, werden Sie Ihr Gedächtnis „verlieren“.Es ist wahrscheinlich nicht sehr wahrscheinlich, dass dies in freier Wildbahn vorkommt, aber es ist leicht zu reproduzieren.

Der große Objektheap ist nicht komprimiert, sodass es durch Fragmentierung zu Speicherverlusten kommen kann.

Es gibt eine Reihe von Objekten, die manuell freigegeben werden müssen.Z.B.Remoting-Objekte ohne Lease und Assemblys (AppDomain muss entladen werden).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top