Laden...

Memoryleak -> Speicher wird nicht freigegeben

Erstellt von fungi35 vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.573 Views
F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 9 Jahren
Memoryleak -> Speicher wird nicht freigegeben

Hallo Leute,

ich bin recht neu in c# und habe ein Anwendung eines Kollegen übernommen.
Ich habe eine Frage zur Speicherverwaltung und bräuchte mal einen Hinweis mit ich in die richtige Richtung komme.

In der Anwendung gibt es eine Positionsbearbeitung mit einem Grid. An das Grid ist eine generische Liste mit den Positionsobjekten gebunden. Wird eine Position bearbeitet wird die entsprechende Position inkl. gesamter Objektstruktur in eine separate Liste (ich nenne sie mal Backup-Liste) kopiert (Deep Copy mittels ICloneable). Eine Position hat ggf. auch zugehörige Unterpositionen die im Laufe der Bearbeitung mit verändert werden, deshalb wohl die Backup-Liste. Die Objektstruktur ist sehr umfangreich, das Clonen aber noch performant.

Wird die Position abgebrochen, werden die Daten aus der Backup-Liste wieder zurück in die originale Liste geschrieben:


        public void RestorePositionen(IList<Model_VorgangPosition> pBackup,
                                      IList<Model_VorgangPosition> pZiel)
        {
            foreach (Model_VorgangPosition position in pBackup)
            {
                Model_VorgangPosition positionzuersetzen = this.Positionen.Where(o => o.Pos.Equals(position.Pos) &&
                                                                                 o.UPos.Equals(position.UPos)).Single();
                int index = this.Positionen.IndexOf(positionzuersetzen);
                this.Positionen[index] = position;
            }
        }

pZiel ist die Liste die an das Grid gebunden ist. pBackup ist die Backup-Liste.
Nach der Übernahme der Daten aus der Backup-Liste wird die Backup-Liste geleert (beim Speichern der Position wird das .Clear())ebenfalls aufgerufen):


this._OldPositionen.Clear();

Mein Problem ist, dass der Speicherverbrauch sehr schnell sehr groß wird (2 GB sind keine Seltenheit), je öfter man Positionen bearbeitet. Dadurch wird die ganze Anwendung insbesondere das Clonen bei einem erneuten Bearbeiten einer Position extrem langsam (anfangs dauert das Clonen 1 Sekunde, später 1 Minute, obwohl sich die Daten gar nicht verändert haben).

Was muss ich alles prüfen um sicherzustellen, dass der Speicher wieder freigegeben wird?
Wie lösche ich Objektreferenzen sicher, so dass die Objekte auch aufgeräumt werden? Mir ist klar, dass der Speicher nicht zwangsläufig und unmittelbar vom GC wieder freigegeben wird, aber diesen hohen und unnötigen Speicherverbrauch würde ich gerne abstellen.
Gibt es einen Unterschied zwischen set objekt = new abc() und set obj = null?

Könnt Ihr mir einen Hinweis in die richtige Richtung geben? Gerne auch, wie man diese ganze Backup-Listen-Geschicht besser lösen kann.

Vielen Dank

Gruß fungi35

16.835 Beiträge seit 2008
vor 9 Jahren

Wenn man etwas klonen muss, dann ist das schon ein erster Hinweis, dass was am Konzept nicht stimmt.
Verwende einen Profiler, der zB in VS 2013 integriert ist, um Bottlenecks zu entdecken und beachte mein Hinweis mit dem Clone().

5.658 Beiträge seit 2006
vor 9 Jahren

Hi fungi35,

gegen das Klonen von Objekten, um einen Zustand zu speichern und wiederherzustellen, ist eigentlich nichts zu sagen. Es ist auch kein Problem, wenn der reservierte Speicher auf 2 GB steigt. Der GC räumt den Speicher leer, wenn er es für richtig hält. Es sei denn, du verwendest nicht verwaltete Ressourcen wie Bitmaps.

Wie hast du denn den Speicherverbrauch gemessen? Mit dem MemoryProfiler oder einfach im TaskManager geschaut?

Christian

Weeks of programming can save you hours of planning

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 9 Jahren

Hallo Abt und MrSparkle,

danke für Eure Antworten 😃
Zugegebenermaßen habe ich es nur im Taskmanager geprüft und nicht mit dem ProcessExplorer.

Das Problem ist, dass die Anwendung sehr sehr langsam wird, sobald der Speicherverbrauch (laut Taskmanager) auf eine bestimmte Größe (bei meinem Tests trat das Problem ab ca. 1,8 GB auf) angewachsen ist. Starte ich die Anwendung neu und führe genau dieselben Aktionen durch, ist die Anwendung sehr performant (Aktionen dauern 1 Sekunde) - aber halt nur bis zu einem gewissen Zeitpunkt (dann dauern dieselben Aktionen 1 Minute).

Ich habe mich auch schon gefragt ob die Restore-Methode richtig umgesetzt ist. Reicht es aus eine Liste mit .Clear() oder = null zu leeren? Werden dabei auch die Referenzen der Child-Objekte der Liste gelöscht? Dazu finde ich bei google, insbesondere bei stackoverflow, unterschiedlichste Aussagen.
Die Positionsobjekte aus der Liste enthalten weitere Listen die an mehrere GridViews gebunden werden. Muss ich diese Listen noch explizit mit .Clear() leeren? Die DataSource der GridViews wird beim Abbrechen der Bearbeitung immer mit GridView.DataSource = new BindingList<...>() überschrieben, so dass eigentlich keine Referenz mehr vorhanden sein dürfte.

Kannst Du mir einen MemoryProfiler empfehlen?

fungi35

16.835 Beiträge seit 2008
vor 9 Jahren

Verwende einen Profiler, der zB in VS 2013 integriert ist

W
872 Beiträge seit 2005
vor 9 Jahren

Du kannst mit Perfview explizit sehen, was der Garbage Collector genau macht (z.B. Generation, Dauer und Statistiken über Speicherfreigabe).

Der Garbage Collector wird nur bei Generation 2 den ganzen Baum aufräumen, dass passiert erst dann, wenn der Speicher knapp wird. Du kannst in Deinem Fall testen, ob Du nach einem

GC.Collect()

bereits die große Verzögerung siehst und wieviel Speicher nach einer vollständigen Garbage Collection übrig ist. Normalerweise macht es nicht Sinn, den Garbage Collector zu forcieren, aber in Deinem Fall hilft es vielleicht mitzuteilen, daß viel aufzuräumen ist.

P
1.090 Beiträge seit 2011
vor 9 Jahren

Hallo funghi35,

wie schon ein paar mal erwähnt, soltest du einen Mermory Provile verwenden. Ohne einblick in den Quellcode können wir da eigendlich nur Raten, wo das Problem ligen könnte.

Vielleicht noch ein paar tips.

Manchmal hielft es sich die Referenzen als Baum mit Wurzel (Main) vorzustellen. Wenn jetzt ein Ast abgesegt wird (Es keine Verbingung mehr zu Wurzel gibt), stierb er und seine Abzweigungen ab. Die Abzweigungen brauchst du nicht mehr expliziet Abzuzägen (NULL zu setzen).

Events sind öfter mal ein Problem, hier ist die Refeerenz genau umgekert. Also vom Anbiter des Events auf dem Abonenten. Das kann zu Problemem sorge wenn der fürhen wenn der Anbieter läbger lebt. Z.B. wenn ich ein Model in einler Listen ansicht habe, es in einer Detail Ansicht anzeige. Die Detail ansicht schließ (NULL setze). Die Events im Model nicht entferne und das Model noch weiter in der Liste dargestellt wird. Jetzt besteht immer noch eine Referenz vom Model auf die Detail Ansicht.

Nicht Verwaltete Resourssen, die musst du selber Freigeben, da es der GC nicht machen kann. Hier kannst du mal schauen ob es Stellen bei dir im Code gibt.

@MrSpackle
Ganz korrekt ist die Aussage nicht, da ein kompletter GC durchgeführt wird bevor ein neuer Speicherbereich Allociert wird (Sollte beim GC 2.0 so gewesen sein und sich beim 4.0 nicht geändert haben, die Block sind 16/32MB groß, wenn ich das jetzt richtig im Kopf habe). Wenn nach einer Aktion der Status Quo für den Speicher wider da sein sollte, ich aber mehrfach die grenze bei wiederholten ausführen der Aktion überschreite, ohne das Speicher freigegeben wird. Liegt die Vermutung nah, das ein Memory Leak vorliegt.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

742 Beiträge seit 2005
vor 9 Jahren

Wenn die Anwendung immer langsamer liegt, liegt das selten an Memory.

Vielleicht wird eine deiner Listen immer länger. Hast du mal überprüft, ob die in der Größe wachsen? Da du hier mit foreach MEHRMALS iterierst (Single(), IndexOf()) könnte hier das Problem liegen.

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 9 Jahren

Hallo Leute,

vielen Dank für die vielen hilfreichen Antwort. Ich bin der Ursache schon ein Stück näher gekommen. Mittels des Memory Profilers habe ich herausgefunden, dass die Generation 2 Collection immer weiter anwächst. Ich komme leider nur sehr langsam voran, weil ich keinerlei Erfahrungen mit Memory Profilern habe und die Objektstruktur sehr komplex ist.

P
1.090 Beiträge seit 2011
vor 9 Jahren

Zum Umgang mit dem Memory Profiler ganz du mit google einiges finden.

Da malignate nicht ganz recht hat, kannst du auch probieren ob du mit dem Performance Profiler was findest. Sehr warscheinlich werden ihrgendwelche Listen (Objekt Bäume) die du durchläufst immer Länger. Was auch immer mehr Speicher verbraucht. Wenn du nur herausfindest was da immer länger braucht, kannst du vieleicht auch herrausfinden wofür der Speicher gebraucht wird.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern