4 Stimmen

Erstellen eines Speicherlecks in C# oder VB.Net

Inspiriert von diese Frage Ich habe mich gefragt, welche Möglichkeiten es gibt, ein Speicherleck in .Net zu erzeugen. Ich habe einmal eines mit ODBC Data Access gefunden. Hat jemand Erfahrungen mit der neuesten Version gemacht?

Edit : Gibt es irgendwelche scheinbar normalen, harmlosen Verwendungen, die Speicherlecks verursachen können?

4voto

Stephen Cleary Punkte 402664

Der einfachste Weg, ein Speicherleck zu erzeugen, ist die missbräuchliche Verwendung von Einrichtungen, die für Interop entwickelt wurden, da sie mit nicht verwaltetem Speicher arbeiten.

Weisen Sie zum Beispiel eine GCHandle auf ein Objekt zeigen und es niemals freigeben.

Bearbeiten: Gibt es irgendwelche scheinbar normalen, harmlosen Anwendungen, die Speicherlecks verursachen können?

Soweit ich weiß, gibt es nur eine, insbesondere in einigen UI-Programmen. Es war möglich, in WinForms, aber erst vor kurzem wurde durch WPF mit MVVM üblich:

Der entscheidende Punkt, den viele Leute übersehen, ist, dass ein Delegat einen Verweis auf das Objekt enthält, auf dem er ausgeführt wird.

Wenn man also ein Muster wie MVVM mit bidirektionalen Bindungen (implementiert mit Ereignissen, bestehend aus Delegaten) verwendet und die Ansicht in eine andere Ansicht mit demselben ViewModel geändert wird, bleiben die Aktualisierungen des ViewModels standardmäßig an beide Ansichten gebunden, was zu einem Leck in der alten Ansicht führt.

Theoretisch kann dies mit jedem Delegierten geschehen, aber in der Praxis ist es nicht üblich, da der Abonnent normalerweise den Herausgeber überlebt oder sich abmeldet.

Eine ähnliche Situation besteht, wenn Lambda-Ereignishandler abgemeldet werden:

timer.Elapsed += (_, __) => myObj.Notify();
timer.Elapsed -= (_, __) => myObj.Notify();

Obwohl die Lambda-Ausdrücke in diesem Fall identisch sind, stellen sie unterschiedliche Handler dar, so dass die Elapsed Ereignis wird weiterhin aufgerufen Notify . Die zweite Zeile oben hat keine Auswirkung; weder wird das Abonnement abbestellt, noch wird ein Fehler ausgelöst.

Beachten Sie, dass unsachgemäße Abmeldungen in der Regel beim Testen erkannt werden, so dass sie nur selten zu "Lecks" im veröffentlichten Code führen. Im Gegensatz dazu verursacht die obige MVVM-Situation keine beobachtbaren Nebeneffekte (außer einem Speicher- und Ressourcenleck).

2voto

Paul Groke Punkte 5864

GCHandle.Alloc() ist eine wunderbare Möglichkeit, "echte" Speicherlecks in .NET zu erzeugen. ("echtes" Leck wie in völlig unerreichbar ohne Hacks/Reflexion nicht erreichbar, aber dennoch undicht)

EDIT

bearbeiten : Gibt es irgendwelche scheinbar normalen, harmlosen Anwendungen, die zu Speicherlecks führen können?

Der Begriff "scheinbar normal" hängt von den eigenen Kenntnissen/Erfahrungen ab.

z.B. System.Windows.Forms.Timer sich selbst "verwurzelt", während es aktiviert ist (eigentlich durch GCHandle.Alloc() ). Wenn Sie eine Timer zu einer Form über den grafischen Editor von Visual Studio, VS wird

  • Hinzufügen einer "Komponenten"-Sammlung zu Ihrer Klasse
  • Code erzeugen, der die Timer zu dieser "Komponenten"-Sammlung
  • Code generieren, der alles in der "Komponenten"-Auflistung im Formular entsorgt Dispose() Methode

Das bedeutet, dass alles wie erwartet funktioniert, ohne Leckage.

Wenn Sie jedoch den Code hinzufügen, der das Programm erstellt und startet Timer Wenn man selbst einen Code einfügt, vergisst man leicht, einen Code hinzuzufügen, der den Vorgang stoppt/deaktiviert. Und Visual Studio wird (kann) das nicht für Sie tun.

In diesem Fall ist die Timer wird am Leben bleiben. Es wird niemals gesammelt werden. Und es wird weiterlaufen (und Ereignisse auslösen). Selbst wenn die Form ist geschlossen/verkauft.

Und da Sie normalerweise die Timer s Tick Ereignis an eine Mitgliedsfunktion der Form die Form wird ebenfalls am Leben erhalten. ( Timer verwurzelt ist, Timer Referenzen Ereignisdelegat, Referenzen Ereignisdelegat Form .)

Da es immer noch viele Menschen gibt, die solche Dinge nicht kennen oder sich nicht dafür interessieren, wird der Code für sie ziemlich "normal" aussehen.

1voto

Wenn Sie sich alle Antworten in Ihrem Link durchlesen, gelten sie so gut wie alle auch für .Net. Während die CLR und die JVM völlig unterschiedliche Systeme sind, sind sie in der Philosophie ihres Designs immer noch recht ähnlich (insbesondere sind sie beide verwaltete Systeme), so dass sie viele der gleichen Stärken und Fallstricke teilen.

1voto

BrokenGlass Punkte 153950

Finalizer-Missbrauch kann zu einem "Speicherleck" führen, d.h. es wird ein Objekt erzeugt, dessen Speicher nie von der GC beansprucht werden kann:

public class Foo
{
    int[] value = new int[100];

    ~Foo()
    {
        GC.ReRegisterForFinalize(this);
    }
}

1voto

Petar Ivanov Punkte 89412

Ein Muster, das man ausnutzen kann, ist eine Situation, in der eine Klasse eine private Sammlung hat, aus der niemand jemals etwas entfernt, aber jemand ständig Objekte hinzufügt.

Angenommen, es gibt einen Hintergrund-Thread, der Elemente aus einer statischen blockierenden Sammlung verarbeiten soll, und der Thread stirbt oder blockiert. Dann kann die Sammlung nur wachsen und ein Leck verursachen:

public class Test
{
    static Test()
    {
        Task.Factory.StartNew(() =>
        {
            Random r = new Random();

            try
            {
                while (true)
                {
                    object o = col.Take();

                    //process o fails at some point
                    if (r.Next(100) == 0)
                    {
                        Console.WriteLine("Fail! No one is processing anymore.");
                        throw new Exception();
                    }
                }
            }
            catch
            {
                Console.WriteLine("We caught the exception, but didn't resume processing");
            }
        });
    }

    private static BlockingCollection<object> col = new BlockingCollection<object>();

    public void Add(object o)
    {
        col.Add(o);
    }
}

class Program {
    public static void Main(string[] args)
    {
        Test t = new Test();
        while (true)
            t.Add(new object());
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X