Welche Strategien und Tools sind nützlich, um Speicherlecks in .NET zu finden?


152

Ich habe 10 Jahre lang C ++ geschrieben. Ich habe Speicherprobleme festgestellt, die jedoch mit vertretbarem Aufwand behoben werden konnten.

In den letzten Jahren habe ich C # geschrieben. Ich finde, ich bekomme immer noch viele Gedächtnisprobleme. Sie sind aufgrund der Nichtbestimmtheit schwer zu diagnostizieren und zu beheben, und weil die C # -Philosophie lautet, dass Sie sich über solche Dinge keine Sorgen machen sollten, wenn Sie dies auf jeden Fall tun.

Ein besonderes Problem ist, dass ich alles im Code explizit entsorgen und bereinigen muss. Wenn ich das nicht tue, helfen die Speicherprofiler nicht wirklich, weil so viel Spreu um Sie herum schwebt. Sie können kein Leck in allen Daten finden, die sie Ihnen zeigen wollen. Ich frage mich, ob ich die falsche Idee habe oder ob das Werkzeug, das ich habe, nicht das beste ist.

Welche Strategien und Tools sind nützlich, um Speicherlecks in .NET zu beheben?


Der Titel Ihres Beitrags stimmt nicht wirklich mit der Frage in Ihrem Beitrag überein. Ich schlage vor, Sie aktualisieren Ihren Titel.
Kevin

Du hast recht. Entschuldigung, ich hatte ein wenig genug von dem aktuellen Leck, das ich jage! Titel aktualisiert.
Scott Langham

3
@ Scott: Haben Sie nicht genug von .NET, es ist nicht das Problem. Ihr Code ist.
GEOCHET

3
Ja, mein Code oder die Bibliotheken von Drittanbietern, die ich gerne benutze.
Scott Langham

@ Scott: Siehe meine Antwort. MemProfiler ist es wert. Wenn Sie es verwenden, erhalten Sie auch ein völlig neues Verständnis der .NET GC-Welt.
GEOCHET

Antworten:


51

Ich verwende den MemProfiler von Scitech, wenn ich einen Speicherverlust vermute.

Bisher habe ich festgestellt, dass es sehr zuverlässig und leistungsstark ist. Es hat meinen Speck bei mindestens einer Gelegenheit gerettet.

Der GC funktioniert in .NET IMO sehr gut, aber genau wie in jeder anderen Sprache oder Plattform passieren schlechte Dinge, wenn Sie schlechten Code schreiben.


3
Ja, ich habe es mit diesem versucht, und es hat mir geholfen, einigen kniffligen Lecks auf den Grund zu gehen. Die größten Lecks, die ich festgestellt hatte, wurden durch Bibliotheken von Drittanbietern in nicht verwaltetem Code verursacht, auf die sie über Interop zugegriffen hatten. Ich war beeindruckt, dass dieses Tool Lecks in nicht verwaltetem Code sowie in verwaltetem Code feststellte.
Scott Langham

1
Ich habe dies als Antwort akzeptiert, weil es am Ende für mich funktioniert hat, aber ich denke, alle anderen Antworten sind sehr nützlich. Übrigens wird dieses Tool häufiger als SciTech Mem Profiler bezeichnet!
Scott Langham

41

Probieren Sie die in diesem Blogbeitrag beschriebene Lösung aus , um das Problem des Vergessens zu vergessen . Hier ist die Essenz:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif

Ich würde es vorziehen, eine Ausnahme anstelle von Debug.Fail
Pedro77

17

Wir haben in unserem Projekt die Software Ants Profiler Pro von Red Gate verwendet. Es funktioniert sehr gut für alle sprachbasierten .NET-Anwendungen.

Wir haben festgestellt, dass der .NET Garbage Collector bei der Bereinigung von In-Memory-Objekten (wie es sein sollte) sehr "sicher" ist. Es würde Objekte in der Nähe halten, nur weil wir es möglicherweise irgendwann in der Zukunft verwenden werden. Dies bedeutete, dass wir bei der Anzahl der Objekte, die wir im Speicher aufgeblasen hatten, vorsichtiger sein mussten. Am Ende haben wir alle unsere Datenobjekte in ein "Inflate-on-Demand" konvertiert (kurz bevor ein Feld angefordert wird), um den Speicheraufwand zu reduzieren und die Leistung zu steigern.

EDIT: Hier ist eine weitere Erklärung dessen, was ich unter "Inflate on Demand" verstehe. In unserem Objektmodell unserer Datenbank verwenden wir Eigenschaften eines übergeordneten Objekts, um die untergeordneten Objekte verfügbar zu machen. Wenn wir beispielsweise einen Datensatz hätten, der eins zu eins auf einen anderen "Detail" - oder "Lookup" -Datensatz verweist, würden wir ihn folgendermaßen strukturieren:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Wir haben festgestellt, dass das oben genannte System einige echte Speicher- und Leistungsprobleme verursacht hat, wenn sich viele Datensätze im Speicher befinden. Deshalb haben wir auf ein System umgestellt, bei dem Objekte nur dann aufgeblasen wurden, wenn sie angefordert wurden, und Datenbankaufrufe nur bei Bedarf durchgeführt wurden:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Dies stellte sich als viel effizienter heraus, da Objekte nicht gespeichert wurden, bis sie benötigt wurden (auf die Get-Methode wurde zugegriffen). Es bot eine sehr große Leistungssteigerung bei der Begrenzung von Datenbanktreffern und einen enormen Gewinn an Speicherplatz.


Ich stimme diesem Produkt zu. Es war einer der besten Profiler, die ich verwendet habe.
Gord

Ich fand den Profiler ziemlich gut, um Leistungsprobleme zu untersuchen. Die Tools zur Speicheranalyse waren jedoch ziemlich schlecht. Ich habe mit diesem Werkzeug ein Leck gefunden, aber es war Unsinn, die Ursache des Lecks zu identifizieren. Und es hilft Ihnen überhaupt nicht, wenn sich das Leck in nicht verwaltetem Code befindet.
Scott Langham

Ok, die neue Version 5.1 ist um einiges besser. Es ist besser, Ihnen zu helfen, die Ursache des Lecks zu finden (obwohl - es gibt immer noch ein paar Probleme, die ANTS mir mitgeteilt hat, dass sie in der nächsten Version behoben werden). Immer noch kein nicht verwalteter Code, aber wenn Sie sich nicht um nicht verwalteten Code kümmern, ist dies jetzt ein ziemlich gutes Tool.
Scott Langham

7

Sie müssen sich immer noch Gedanken über den Speicher machen, wenn Sie verwalteten Code schreiben, es sei denn, Ihre Anwendung ist trivial. Ich werde zwei Dinge vorschlagen: Lesen Sie zuerst CLR über C #, da dies Ihnen hilft, die Speicherverwaltung in .NET zu verstehen. Zweitens lernen Sie, ein Tool wie CLRProfiler (Microsoft) zu verwenden. Dies kann Ihnen eine Vorstellung davon geben, was Ihren Speicherverlust verursacht (z. B. können Sie einen Blick auf die Fragmentierung Ihres großen Objekthaufens werfen).


Ja. CLRPRofiler ist ziemlich cool. Es kann mit Informationen etwas explosiv werden, wenn Sie versuchen, die Ansicht der zugewiesenen Objekte zu durchsuchen, aber alles ist vorhanden. Es ist definitiv ein guter Ausgangspunkt, zumal es kostenlos ist.
Scott Langham

6

Verwenden Sie nicht verwalteten Code? Wenn Sie laut Microsoft keinen nicht verwalteten Code verwenden, sind Speicherverluste im herkömmlichen Sinne nicht möglich.

Der von einer Anwendung verwendete Speicher wird jedoch möglicherweise nicht freigegeben, sodass die Speicherzuordnung einer Anwendung während der gesamten Lebensdauer der Anwendung zunehmen kann.

Von So identifizieren Sie Speicherlecks in der Common Language Runtime bei Microsoft.com

In einer .NET Framework-Anwendung kann ein Speicherverlust auftreten, wenn Sie nicht verwalteten Code als Teil der Anwendung verwenden. Dieser nicht verwaltete Code kann Speicherverlust verursachen, und die .NET Framework-Laufzeit kann dieses Problem nicht beheben.

Darüber hinaus scheint ein Projekt nur einen Speicherverlust zu haben. Diese Bedingung kann auftreten, wenn viele große Objekte (z. B. DataTable-Objekte) deklariert und dann einer Sammlung (z. B. einem DataSet) hinzugefügt werden. Die Ressourcen, die diese Objekte besitzen, werden möglicherweise nie freigegeben, und die Ressourcen bleiben für den gesamten Programmlauf erhalten. Dies scheint ein Leck zu sein, aber tatsächlich ist es nur ein Symptom für die Art und Weise, wie Speicher im Programm zugewiesen wird.

Um diese Art von Problem zu lösen , können Sie IDisposable implementieren . Wenn Sie einige der Strategien für den Umgang mit Speicherverwaltung sehen möchten, würde ich vorschlagen, nach IDisposable, XNA und Speicherverwaltung zu suchen, da Spieleentwickler eine vorhersehbarere Speicherbereinigung benötigen und den GC daher zwingen müssen, seine Aufgabe zu erledigen.

Ein häufiger Fehler besteht darin, Ereignishandler, die ein Objekt abonnieren, nicht zu entfernen. Ein Event-Handler-Abonnement verhindert, dass ein Objekt recycelt wird. Schauen Sie sich auch die using- Anweisung an, mit der Sie einen begrenzten Bereich für die Lebensdauer einer Ressource erstellen können.


5
Siehe blogs.msdn.com/tess/archive/2006/01/23/… . Es spielt keine Rolle, ob ein Speicherverlust "traditionell" ist oder nicht, es ist immer noch ein Verlust.
Constantin

2
Ich verstehe Ihren Standpunkt - aber eine ineffiziente Zuweisung und Wiederverwendung von Speicher durch ein Programm unterscheidet sich von einem Speicherverlust.
Timothy Lee Russell

Gute Antwort, danke, dass Sie sich daran erinnert haben, dass Event-Handler gefährlich sein können.
Frameworkninja

3
@ Timothy Lee Russel: Wenn eine unbegrenzte (1) Speichermenge gleichzeitig zugewiesen (verwurzelt) werden kann, nachdem sie unbrauchbar geworden ist (2), ohne dass irgendetwas im System die Informationen und Impulse hat, die erforderlich sind, um sie rechtzeitig zu entfernen, ist dies ein Speicherverlust . Selbst wenn der Speicher eines Tages freigegeben wird, wenn sich genug nutzloses Material ansammelt, um das System zu drosseln, bevor dies geschieht, ist es ein Leck. (1) Größer als O (N), wobei N der Betrag der nützlichen Zuweisung ist; (2) Dinge sind nutzlos, wenn das Entfernen von Verweisen darauf die Programmfunktionalität nicht beeinträchtigen würde.
Supercat

2
@ Timothy Lee Russel: Das normale "Speicherleck" -Muster tritt auf, wenn Speicher von einer Entität im Namen einer anderen Entität gehalten wird , wobei erwartet wird, dass er informiert wird, wenn er nicht mehr benötigt wird, diese jedoch die Entität aufgibt, ohne die erste zu informieren. Die Entität, die den Speicher hält, braucht ihn nicht wirklich, aber es gibt keine Möglichkeit, dies festzustellen.
Supercat

5

Dieser Blog enthält einige wirklich wundervolle exemplarische Vorgehensweisen, bei denen Windbg und andere Tools verwendet werden, um Speicherlecks aller Art aufzuspüren. Hervorragende Lektüre, um Ihre Fähigkeiten zu entwickeln.


5

Ich hatte gerade einen Speicherverlust in einem Windows-Dienst, den ich behoben habe.

Zuerst habe ich MemProfiler ausprobiert . Ich fand es sehr schwer zu bedienen und überhaupt nicht benutzerfreundlich.

Dann habe ich JustTrace verwendet, das einfacher zu verwenden ist und Ihnen mehr Details zu den Objekten gibt, die nicht richtig angeordnet sind.

Dadurch konnte ich das Speicherleck sehr einfach beheben.


3

Wenn die beobachteten Lecks auf eine außer Kontrolle geratene Cache-Implementierung zurückzuführen sind, ist dies ein Szenario, in dem Sie möglicherweise die Verwendung von WeakReference in Betracht ziehen sollten. Dies kann dazu beitragen, dass bei Bedarf Speicher freigegeben wird.

Meiner Meinung nach ist es jedoch besser, eine maßgeschneiderte Lösung in Betracht zu ziehen - nur Sie wissen wirklich, wie lange Sie die Objekte in der Nähe halten müssen. Daher ist das Entwerfen eines geeigneten Housekeeping-Codes für Ihre Situation normalerweise der beste Ansatz.


3

Ich bevorzuge Dotmemory von Jetbrains


Sie können der einzige sein :)
HellBaby

Ich habe es auch versucht. Ich denke, das ist ein gutes Werkzeug. Einfach zu bedienen, informativ. Integriert in Visual Studio
Redeye

In unserem Fall ist das Visual Studio-Snapshot-Tool bei der Fehlerbehebung bei Speicherlecks abgestürzt / hat keinen Snapshot erstellt. Dotmemory blieb cool und handhabte (scheinbar) problemlos mehrere Schnappschüsse von mehr als 3 GB.
Michael Kargl

3

Big Guns - Debugging-Tools für Windows

Dies ist eine erstaunliche Sammlung von Werkzeugen. Sie können damit sowohl verwaltete als auch nicht verwaltete Heaps analysieren und offline ausführen. Dies war sehr praktisch für das Debuggen einer unserer ASP.NET-Anwendungen, die aufgrund von Speicherüberlastung weiterhin recycelt wurden. Ich musste nur einen vollständigen Speicherauszug des lebenden Prozesses erstellen, der auf dem Produktionsserver ausgeführt wurde. Alle Analysen wurden offline in WinDbg durchgeführt. (Es stellte sich heraus, dass einige Entwickler den In-Memory-Sitzungsspeicher überlasteten.)

"Wenn kaputt ist es ..." Blog hat sehr nützliche Artikel zu diesem Thema.


2

Das Beste, was Sie beachten sollten, ist, die Verweise auf Ihre Objekte zu verfolgen. Es ist sehr einfach, Verweise auf Objekte aufzuhängen, die Sie nicht mehr interessieren. Wenn Sie etwas nicht mehr verwenden, entfernen Sie es.

Gewöhnen Sie sich daran, einen Cache-Anbieter mit verschiebbaren Ablaufzeiten zu verwenden, damit etwas, auf das für ein gewünschtes Zeitfenster nicht verwiesen wird, dereferenziert und bereinigt wird. Wenn jedoch häufig darauf zugegriffen wird, wird dies im Speicher angezeigt.


2

Eines der besten Tools ist die Verwendung der Debugging-Tools für Windows und die Erstellung eines Speicherauszugs des Prozesses mithilfe von adplus . Anschließend können Sie mithilfe von windbg und dem sos- Plugin den Prozessspeicher, die Threads und die Aufrufstapel analysieren.

Sie können diese Methode verwenden, um Probleme auch auf Servern zu identifizieren. Nachdem Sie die Tools installiert haben, geben Sie das Verzeichnis frei, stellen Sie dann mithilfe von (Net Use) eine Verbindung zur Freigabe vom Server her und führen Sie entweder einen Absturz oder einen Hang-Dump des Prozesses durch.

Dann offline analysieren.


Ja, dies funktioniert gut, insbesondere bei fortgeschritteneren Aufgaben oder bei der Diagnose von Problemen in freigegebener Software, an die Sie einen Debugger nicht einfach anschließen können. Dieser Blog hat viele Tipps zur Verwendung dieser Tools gut: blogs.msdn.com/tess
Scott Langham

2

Nach einem meiner Fixes für verwaltete Anwendungen hatte ich das gleiche Problem, wie ich überprüfen konnte, ob meine Anwendung nach meiner nächsten Änderung nicht denselben Speicherverlust aufweist. Ich habe also so etwas wie das Framework zur Überprüfung der Objektfreigabe geschrieben. Schauen Sie sich das an das NuGet-Paket ObjectReleaseVerification . Ein Beispiel finden Sie hier https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample und Informationen zu diesem Beispiel http://outcoldman.ru/en/blog/show/322


0

In Visual Studio 2015 sollten Sie ein sofort einsatzbereites Diagnosetool für die Speichernutzung verwenden, um Daten zur Speichernutzung zu erfassen und zu analysieren.

Mit dem Tool zur Speichernutzung können Sie einen oder mehrere Snapshots des verwalteten und nativen Speicherheaps erstellen, um die Auswirkungen der Objektnutzung auf die Speichernutzung zu verstehen.


0

Eines der besten Tools, das ich mit DotMemory verwendet habe. Sie können dieses Tool als Erweiterung in VS verwenden. Nachdem Sie Ihre App ausgeführt haben, können Sie jeden Teil des Speichers (nach Objekt, NameSpace usw.) analysieren, den Ihre App verwendet, und einen Schnappschuss davon erstellen Vergleichen Sie es mit anderen SnapShots. DotMemory

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.