.net - Anatomie eines "Gedächtnislecks"

Translate

In der .NET-Perspektive:

  • Was ist einSpeicherleck?
  • Wie können Sie feststellen, ob Ihre Anwendung undicht ist? Was sind die Auswirkungen?
  • Wie können Sie einen Speicherverlust verhindern?
  • Wenn Ihre Anwendung einen Speicherverlust aufweist, verschwindet dieser, wenn der Prozess beendet oder beendet wird? Oder wirken sich Speicherverluste in Ihrer Anwendung auch nach Abschluss des Prozesses auf andere Prozesse im System aus?
  • Und was ist mit nicht verwaltetem Code, auf den über COM Interop und / oder P / Invoke zugegriffen wird?
This question and all comments follow the "Attribution Required."

Alle Antworten

Translate

Die beste Erklärung, die ich gesehen habe, ist in Kapitel 7 der kostenlosenGrundlagen der Programmierung E-Book.

Grundsätzlich in.NETZEin Speicherverlust tritt auf, wenn referenzierte Objekte verwurzelt sind und daher kein Müll gesammelt werden kann. Dies tritt versehentlich auf, wenn Sie an Referenzen festhalten, die über den beabsichtigten Bereich hinausgehen.

Sie werden wissen, dass Sie Lecks haben, wenn Sie OutOfMemoryExceptions erhalten oder Ihre Speichernutzung über das hinausgeht, was Sie erwarten (PerfMonhat schöne Speicherzähler).

Verstehen.NETZDas Speichermodell ist der beste Weg, dies zu vermeiden. Um zu verstehen, wie der Garbage Collector funktioniert und wie Referenzen funktionieren, verweise ich Sie erneut auf Kapitel 7 des E-Books. Denken Sie auch an häufige Fallstricke, wahrscheinlich die häufigsten Ereignisse. Wenn ObjektAist für ein Ereignis auf Objekt registriertB, dann ObjektAbleibt bis zum ObjektBverschwindet weilBenthält einen Verweis aufA. Die Lösung besteht darin, die Registrierung Ihrer Ereignisse aufzuheben, wenn Sie fertig sind.

Mit einem guten Speicherprofil können Sie natürlich Ihre Objektdiagramme anzeigen und die Verschachtelung / Referenzierung Ihrer Objekte untersuchen, um festzustellen, woher Referenzen stammen und welches Stammobjekt verantwortlich ist (Red-Gate-Ameisenprofil, JetBrains dotMemory,memprofilersind wirklich gute Entscheidungen, oder Sie können nur den Text verwendenWinDbgundSOS, aber ich würde dringend ein kommerzielles / visuelles Produkt empfehlen, es sei denn, Sie sind ein echter Guru.

Ich glaube, dass nicht verwalteter Code seinen typischen Speicherlecks unterliegt, mit der Ausnahme, dass gemeinsam genutzte Referenzen vom Garbage Collector verwaltet werden. In diesem letzten Punkt könnte ich mich irren.

Quelle
Translate

Genau genommen verbraucht ein Speicherverlust Speicher, der vom Programm "nicht mehr verwendet" wird.

"Nicht mehr verwendet" hat mehr als eine Bedeutung, es könnte "kein Verweis mehr darauf" bedeuten, dh völlig nicht wiederherstellbar, oder es könnte bedeuten, referenziert, wiederherstellbar, nicht verwendet, aber das Programm behält die Verweise trotzdem bei. Nur das letztere gilt für .Net fürperfekt verwaltete Objekte. Es sind jedoch nicht alle Klassen perfekt, und irgendwann kann eine zugrunde liegende nicht verwaltete Implementierung dauerhaft Ressourcen für diesen Prozess verlieren.

In allen Fällen verbraucht die Anwendung mehr Speicher als unbedingt erforderlich. Die Nebenwirkungen können, abhängig von der Menge, die durchgesickert ist, von keiner über eine durch übermäßige Erfassung verursachte Verlangsamung bis zu einer Reihe von Speicherausnahmen und schließlich zu einem schwerwiegenden Fehler, gefolgt von einer erzwungenen Prozessbeendigung, reichen.

Sie wissen, dass eine Anwendung ein Speicherproblem hat, wenn die Überwachung zeigt, dass Ihrem Prozess immer mehr Speicher zugewiesen wirdnach jedem Speicherbereinigungszyklus. In diesem Fall behalten Sie entweder zu viel Speicher oder eine zugrunde liegende nicht verwaltete Implementierung ist undicht.

Bei den meisten Lecks werden Ressourcen wiederhergestellt, wenn der Prozess beendet wird. Einige Ressourcen werden jedoch in bestimmten Fällen nicht immer wiederhergestellt. GDI-Cursor-Handles sind dafür berüchtigt. Wenn Sie über einen Interprozess-Kommunikationsmechanismus verfügen, wird der im anderen Prozess zugewiesene Speicher natürlich erst freigegeben, wenn dieser Prozess ihn freigibt oder beendet.

Quelle
Translate

Ich denke, die Fragen "Was ist ein Speicherverlust?" Und "Was sind die Auswirkungen?" Wurden bereits gut beantwortet, aber ich wollte den anderen Fragen noch ein paar Dinge hinzufügen ...

So verstehen Sie, ob Ihre Anwendung undicht ist

Ein interessanter Weg ist zu öffnenperfmonund füge Spuren für hinzu# Bytes in allen Heapsund# Gen 2 Sammlungenin jedem Fall nur Ihren Prozess betrachten. Wenn das Ausüben eines bestimmten Features dazu führt, dass sich die Gesamtzahl der Bytes erhöht und dieser Speicher nach der nächsten Gen 2-Sammlung zugewiesen bleibt, kann man sagen, dass das Feature Speicher verliert.

Wie man etwas vorbeugt

Andere gute Meinungen wurden abgegeben. Ich würde nur hinzufügen, dass vielleicht dieam häufigsten übersehenUrsache für .NET-Speicherlecks ist das Hinzufügen von Ereignishandlern zu Objekten, ohne diese zu entfernen. Ein an ein Objekt angehängter Ereignishandler ist eine Art Referenz auf dieses Objekt und verhindert so die Erfassung, selbst nachdem alle anderen Referenzen verschwunden sind. Denken Sie immer daran, Ereignishandler zu trennen (mithilfe der-=Syntax in C #).

Verschwindet das Leck, wenn der Prozess beendet wird, und was ist mit COM Interop?

Wenn Ihr Prozess beendet wird, wird der gesamte in seinen Adressraum abgebildete Speicher vom Betriebssystem zurückgefordert, einschließlich aller COM-Objekte, die von DLLs bereitgestellt werden. COM-Objekte können vergleichsweise selten aus separaten Prozessen bedient werden. In diesem Fall sind Sie beim Beenden Ihres Prozesses möglicherweise weiterhin für den in den von Ihnen verwendeten COM-Server-Prozessen zugewiesenen Speicher verantwortlich.

Quelle
Translate

Ich würde Speicherlecks als ein Objekt definieren, das nicht den gesamten nach dem Abschluss zugewiesenen Speicher freigibt. Ich habe festgestellt, dass dies in Ihrer Anwendung passieren kann, wenn Sie Windows-API und COM (dh nicht verwalteten Code, der einen Fehler enthält oder nicht ordnungsgemäß verwaltet wird), im Framework und in Komponenten von Drittanbietern verwenden. Ich habe auch festgestellt, dass das Problem nicht verursacht werden kann, nachdem bestimmte Objekte wie Stifte verwendet wurden.

Ich persönlich habe Ausnahmen aufgrund von Speichermangel erlitten, die verursacht werden können, aber nicht nur Speicherlecks in Dot-Net-Anwendungen betreffen. (OOM kann auch von Pinning kommen, siehePinning Artical). Wenn Sie keine OOM-Fehler erhalten oder bestätigen müssen, ob es sich um einen Speicherverlust handelt, der ihn verursacht, besteht die einzige Möglichkeit darin, Ihre Anwendung zu profilieren.

Ich würde auch versuchen, Folgendes sicherzustellen:

a) Alles, was Idisposable implementiert, wird entweder mit einem finally-Block oder mit der using-Anweisung entsorgt, zu der Pinsel, Stifte usw. gehören (einige Leute argumentieren, alles zusätzlich auf nichts zu setzen).

b) Alles, was eine close-Methode hat, wird mit finally oder der using-Anweisung wieder geschlossen (obwohl ich festgestellt habe, dass using nicht immer geschlossen wird, je nachdem, ob Sie das Objekt außerhalb der using-Anweisung deklariert haben).

c) Wenn Sie nicht verwaltete Code- / Windows-APIs verwenden, werden diese anschließend korrekt behandelt. (Einige haben Bereinigungsmethoden zum Freigeben von Ressourcen)

Hoffe das hilft.

Quelle
Translate

Wenn Sie einen Speicherverlust in .NET diagnostizieren müssen, überprüfen Sie diese Links:

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

In diesen Artikeln wird beschrieben, wie Sie einen Speicherauszug Ihres Prozesses erstellen und analysieren, damit Sie zunächst feststellen können, ob Ihr Leck nicht verwaltet oder verwaltet wird, und ob es verwaltet wird und wie Sie herausfinden, woher es stammt.

Microsoft hat auch ein neueres Tool namens DebugDiag, das beim Generieren von Crash-Dumps hilft und ADPlus ersetzt.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=de

Quelle
Translate

Verwenden von CLR Profiler von Microsofthttp://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=deDies ist eine hervorragende Möglichkeit, um festzustellen, welche Objekte Speicher enthalten, welcher Ausführungsfluss zur Erstellung dieser Objekte führt, und um zu überwachen, welche Objekte wo auf dem Heap leben (Fragmentierung, LOH usw.).

Quelle
Translate

Die beste Erklärung für die Funktionsweise des Müllsammlers ist Jeff RichtersCLR über C #Buch, (Kap. 20). Das Lesen bietet eine gute Grundlage für das Verständnis, wie Objekte bestehen bleiben.

Eine der häufigsten Ursachen für das versehentliche Rooten von Objekten ist das Anschließen von Ereignissen außerhalb einer Klasse. Wenn Sie ein externes Ereignis anschließen

z.B

SomeExternalClass.Changed += new EventHandler(HandleIt);

und vergessen Sie, sich davon zu lösen, wenn Sie entsorgen, dann hat SomeExternalClass einen Verweis auf Ihre Klasse.

Wie oben erwähnt, ist dieSciTech-Speicherprofilerist hervorragend darin, Ihnen Wurzeln von Objekten zu zeigen, von denen Sie vermuten, dass sie undicht sind.

Es gibt aber auch eine sehr schnelle Möglichkeit, einen bestimmten Typ zu überprüfen, indem Sie einfach WnDBG verwenden (Sie können dies sogar im VS.NET-Sofortfenster verwenden, während Sie angehängt sind):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Tun Sie jetzt etwas, von dem Sie glauben, dass es die Objekte dieses Typs entsorgt (z. B. ein Fenster schließen). Hier ist es praktisch, irgendwo eine Debug-Schaltfläche zu haben, die ausgeführt wirdSystem.GC.Collect()ein paar Mal.

Dann renne!dumpheap -stat -type <TypeName>nochmal. Wenn die Zahl nicht oder nicht so stark gesunken ist, wie Sie es erwartet haben, haben Sie eine Grundlage für weitere Untersuchungen. (Ich habe diesen Tipp von einem Seminar von erhaltenIngo Rammer).

Quelle
Translate

Ich denke, in einer verwalteten Umgebung wäre ein Leck, wenn Sie einen unnötigen Verweis auf einen großen Speicherblock behalten würden.

Quelle
Translate

Warum denken die Leute, dass ein Speicherverlust in .NET nicht mit einem anderen Verlust identisch ist?

Ein Speicherverlust tritt auf, wenn Sie eine Verbindung zu einer Ressource herstellen und diese nicht loslassen. Sie können dies sowohl in verwalteter als auch in nicht verwalteter Codierung tun.

In Bezug auf .NET und andere Programmiertools gab es Ideen zur Speicherbereinigung und andere Möglichkeiten zur Minimierung von Situationen, die zu einem Leck Ihrer Anwendung führen. Die beste Methode, um Speicherlecks zu vermeiden, besteht darin, dass Sie Ihr zugrunde liegendes Speichermodell und die Funktionsweise auf der von Ihnen verwendeten Plattform verstehen müssen.

Zu glauben, dass GC und andere Magie Ihr Chaos beseitigen, ist der kurze Weg zu Speicherlecks und wird später schwer zu finden sein.

Wenn Sie die Codierung nicht verwalten, stellen Sie normalerweise sicher, dass Sie bereinigen. Sie wissen, dass die Ressourcen, die Sie in Anspruch nehmen, in Ihrer Verantwortung für die Bereinigung liegen und nicht für die des Hausmeisters.

In .NET hingegen denken viele Leute, dass der GC alles bereinigen wird. Nun, es tut etwas für Sie, aber Sie müssen sicherstellen, dass es so ist. .NET verpackt viele Dinge, sodass Sie nicht immer wissen, ob es sich um eine verwaltete oder nicht verwaltete Ressource handelt, und Sie müssen sicherstellen, womit Sie es zu tun haben. Der Umgang mit Schriftarten, GDI-Ressourcen, Active Directory, Datenbanken usw. ist normalerweise wichtig.

In verwalteten Begriffen werde ich meinen Hals auf die Linie legen, um zu sagen, dass er verschwindet, sobald der Prozess beendet / entfernt wird.

Ich sehe jedoch, dass viele Leute dies haben, und ich hoffe wirklich, dass dies enden wird. Sie können den Benutzer nicht bitten, Ihre App zu beenden, um Ihr Chaos zu beseitigen! Schauen Sie sich einen Browser an, der IE, FF usw. sein kann, öffnen Sie beispielsweise Google Reader, lassen Sie ihn einige Tage lang stehen und sehen Sie sich an, was passiert.

Wenn Sie dann einen anderen Tab im Browser öffnen, zu einer Site surfen und dann den Tab schließen, auf dem sich die andere Seite befand, auf der der Browser leckte, wird der Browser Ihrer Meinung nach den Speicher freigeben? Nicht so beim IE. Auf meinem Computer verbraucht der Internet Explorer in kurzer Zeit (ca. 3-4 Tage) problemlos 1 GB Speicher, wenn ich Google Reader verwende. Einige Zeitungen sind noch schlimmer.

Quelle
Translate

Ich denke, in einer verwalteten Umgebung wäre ein Leck, wenn Sie einen unnötigen Verweis auf einen großen Speicherblock behalten würden.

Absolut. Wenn Sie die .Dispose () -Methode bei Bedarf nicht für Einwegobjekte verwenden, kann dies zu Memlecks führen. Der einfachste Weg, dies zu tun, ist mit einem using-Block, da er am Ende automatisch .Dispose () ausführt:

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

Wenn Sie eine Klasse erstellen, die nicht verwaltete Objekte verwendet, und IDisposable nicht korrekt implementieren, kann dies zu Speicherverlusten für die Benutzer Ihrer Klasse führen.

Quelle
Translate

Alle Speicherlecks werden durch Programmbeendigung behoben.

Es fehlt genügend Speicher, und das Betriebssystem kann entscheiden, das Problem in Ihrem Namen zu beheben.

Quelle
Pat
Translate

Ich werde Bernard in .net zustimmen, was ein Mem-Leck wäre.

Sie können Ihre Anwendung profilieren, um die Speichernutzung zu sehen, und feststellen, dass sie ein Leck aufweist, wenn sie viel Speicher verwaltet, wenn dies nicht der Fall sein sollte.

In verwalteten Begriffen werde ich meinen Hals auf die Linie legen, um zu sagen, dass er verschwindet, sobald der Prozess beendet / entfernt wird.

Nicht verwalteter Code ist sein eigenes Biest, und wenn ein Leck darin vorhanden ist, folgt er einem Standardmem. Leckdefinition.

Quelle
Translate

Beachten Sie auch, dass .NET zwei Heaps hat, von denen einer der Heap für große Objekte ist. Ich glaube, dass Objekte von ungefähr 85.000 oder mehr auf diesen Haufen gelegt werden. Dieser Heap hat andere Lebenszeitregeln als der reguläre Heap.

Wenn Sie große Speicherstrukturen (Wörterbücher oder Listen) erstellen, ist es ratsam, nach den genauen Regeln zu suchen.

Soweit der Speicher bei Prozessbeendigung wiederhergestellt wird, wird bei Beendigung von Win98 alles an das Betriebssystem zurückgegeben, es sei denn, Sie führen Win98 aus oder es entspricht. Die einzigen Ausnahmen sind Dinge, die prozessübergreifend geöffnet werden und bei einem anderen Prozess ist die Ressource noch offen.

COM-Objekte können schwierig sein. Wenn Sie immer die verwendenIDisposeMuster, werden Sie sicher sein. Aber ich bin auf einige Interop-Assemblys gestoßen, die implementiert werdenIDispose. Der Schlüssel hier ist anzurufenMarshal.ReleaseCOMObjectwenn du damit fertig bist. Die COM-Objekte verwenden weiterhin die Standard-COM-Referenzzählung.

Quelle
Translate

ich fand.Net Memory ProfilerEine sehr gute Hilfe beim Auffinden von Speicherlecks in .Net. Es ist nicht kostenlos wie der Microsoft CLR Profiler, aber meiner Meinung nach schneller und auf den Punkt gebracht. EIN

Quelle
Translate

Eine Definition ist:Nicht erreichbarer Speicher kann nicht freigegeben werden, der während der Ausführung des Zuweisungsprozesses nicht mehr einem neuen Prozess zugewiesen werden kann. Es kann meistens mit GC-Techniken geheilt oder mit automatisierten Werkzeugen erkannt werden.

Für weitere Informationen, besuchen Sie bittehttp://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.

Quelle