EF und ne Menge Schrott im RAM
-
Hallo,
ich habe ein enormes Problem in meiner Anwendung.
private void MitarbeiterAktualisierenDoWork() { lock (_locker) { try { if(!_firstloadMitarbeiter) FbDispoplanContext.Refresh(RefreshMode.StoreWins, FbDispoplanContext.MITARBEITER); MitarbeiterListe = new SqlceTable<MITARBEITER>(FbDispoplanContext.MITARBEITER); DisponentCollection = new ObservableCollection<MitarbeiterComboBoxItem>(_mitarbeitertyp(FbDispoplanContext, 0)); FahrerCollection = new ObservableCollection<MitarbeiterComboBoxItem>(_mitarbeitertyp(FbDispoplanContext, 1)); OnPropertyChanged("Mitarbeitergeladen"); } catch (Exception ex) { WriteErrorMessage(ex, EventLogEntryType.Error, true); } } }
Ich habe in meinem ViewModel eine Instanz von meinem ObjectContext. Es gibt in der View nun einen Button, mit dem man ein DataGrid aktualisieren kann. Wie er aktualisiert, sieht man im Code oben.
Problem ist nun, das der Speicherverbrauch der Anwendung enorm ansteigt wenn ich mehrmals aktualisiere. Sprich, er gibt keinen Speicherbereich frei und da können dann ganz schnell aus 250MB 1,4GB werden!!
Wer hat eine Lösung?
-
Kontrollier mal dass alle referenzen auf deine Daten gelöst werden, sonnst räumt der cc nicht auf.
Das können auch referenzen vom DataGrid selber sein.Du machst da neue Instanzen von Objekten. Der DataGrid wird aber vermutlich noch die Referenz auf das alte Objekt haben, welches jetzt halt nicht mehr in deiner Variable, aber noch auf dem Ram sitzt.
Evt. Nützt es etwas, wenn du beim Aktualisieren zu erst die Daten vom DataGrid löst, danach änderst und dann wieder anbindest.
Edit (01.05.2012):
Warum verwendest du da lock auf _locker?
_locker wird in dem codeabschnitt gar nie verwendet oder irre ich mich da?Grüsse
Chiller
-
Und wie hast du den Speicherverbrauch gemessen? Ich hoffe doch nicht mit dem Taskmanager?
Grüssli
-
Hallo,
sorry, dass ich etwas länger gebraucht habe. Also mal eben zu euren Antworten.
Also ich denke auch, dass es was mit Referenzen zu tun hat. Die Daten habe ich an ein ItemsControl gebunden. Ich hab das ItemsControl mal gegen eine ListView ausgetauscht, die ich modifiziert habe. Ich war erschrocken, dass schon mal 300MB(!!) an RAM mehr frei waren. Nach ein bisschen googlen habe ich dann auch mal festgestellt, dass man doch lieber die ListView nehmen sollte(in Verbindung mit Virtualisierung). Und da die sogar noch ein paar coole Features hat, werde ich in Zukunft die Finger von dem ItemsControl lassen!
Das ist schon mal der Erste Ansatz einer Lösung. Ich bin von Anfang an schmaler im RAM ABER das Problem besteht weiterhin. Sprich, wenn ich Aktualisiere dann wächst und wächst die Anwendung.Ich habe auch schon mal versucht "MitarbeiterListe" auf "null" zu setzen. Aber das hat nicht sonderlich viel gebracht. Oder muss zwischendurch die Überfläche informiert werden(sprich über ein Update)?
Bin ich denn der einzige, der dieses Problem hat? Mittlerweile kann man ja sagen, dass es vielleicht nicht ganz am EF liegt sondern an dem(den) Control(s).
Natürlich habe ich mir das angeschaut, was mir mein Task Manager sagt. Ich habe auch kein Tool gefunden, mit dem ich mal während der Laufzeit die Größen der Objekte sehen kann. Unterm Strich zähhle aber was im RAM liegt. Ansonsten kann bei zig Benutzern die Anwendung einfach nicht ordentlich auf den Terminals laufen wenn die im RAM explodiert:-(
Wie macht ihr das denn mit den Binding?
-
secondsun schrieb:
Ich habe auch schon mal versucht "MitarbeiterListe" auf "null" zu setzen. Aber das hat nicht sonderlich viel gebracht. Oder muss zwischendurch die Überfläche informiert werden(sprich über ein Update)?
Die Variable auf null setzen reicht noch nicht. Die Speicherverwaltung ist etwas komplexer, als du es dir anscheinend vorstellst
Der Garbage Collector arbeitet asynchron zu deinem Programm. Er räumt somit von Zeit zu Zeit den Speicher auf. Das kann erst viel später passieren, nachdem du die Variable aufnull
gesetzt hast. Das Thema ist ziemlich komplex.
Des Weiteren hast du das Problem, dass auch wenn der GC den Speicher aufgeräumt hat, er womöglich keinen Speicher dem Betriebsystem zurückgibt. Der GC hält den Speicher also für deinen Prozess reserviert, falls du später erneut speicher benötigst, verwendet er zuerst diesen, bevor er erneut Speicher vom Betriebsystem holen muss. Daher stimmen auch die Werte nicht im Task Manager, weil du damit eigentlich falsche Werte misst. Du misst mistsecondsun schrieb:
Ich habe auch kein Tool gefunden, mit dem ich mal während der Laufzeit die Größen der Objekte sehen kann.
Das Problem dürfte sein, dass es da kaum Tools gibt und es eher schwergewichtige Programme sind, welche die Analyzen durchführen. Ein bekannter vertreter dürfte dotTrace von Jetbrains sein. Die persönliche Lizenz nur für die Speicheranalyse kostet 149$
Gibt aber auch ein zwei freie Programme. Zum Beispiel darf man wohl den CLR Profiler von Microsoft erwähnen. Der sollte dir zumindest helfen dem Problem auf die Schliche zu kommen. Auch gibt es von Microsoft noch die WPF Performance Suite.Es gibt natürlich auch noch ein paar Klassen im .Net Framework, welche dir womöglich weiterhelfen können. Sie sollten aber mit Vorsicht verwendet werden. So gibt es die Klasse
System.GC
mit den MethodenCollect
oderGetTotalMemory
. Was du bei diesen Methoden beachten solltest:Collect
solltest du in produktiven Code nie verwenden!GetTotalMemory
liefert nur eine Schätzung zurück!
Es gibt auch noch bei der Klasse
Process
Möglichkeiten zur Abfrage der verschiedenen Speichermengen. ÜberGetCurrentProcess
holst du den aktuellen Prozess. Was du hier beachten solltest:
1. Die Werte werden zwischengespeichert. Du musstRefresh
aufrufen, um die aktuellen Werte zu erhalten.
2. Du solltest dir unbedingt im klaren darüber sein, was die unterschiedlichen Speichergrössen bedeuten. Sonst misst du wieder mistIch hoffe, dass dich dies zumindest etwas weiter bringt.
Grüssli
-
secondsun schrieb:
... dass man doch lieber die ListView nehmen sollte(in Verbindung mit Virtualisierung). Und da die sogar noch ein paar coole Features hat, werde ich in Zukunft die Finger von dem ItemsControl lassen!
Die ListView ist auch ein ItemsControl, da wird nur das ItemsPanelTemplate auf den VirtualizedStackPanel gesetzt
PS. Ich seh in dein Code "MitarbeiterComboBoxItem".
Finde ich merkwürdig, du hällst direkt ComboBoxItems? Hast du keine Code/UI Trennung? Halte doch einfach die Objekte, der entsprechende Container wird von WPF dann schon von allein erstellt. Wenn es schon nur DTOs sind, ist "ComboBoxItem" ein schlechter Name, da es den Code egal sein sollte wo es gehostet wird.
-
Hmmm, VisualStudio 2010 bietet einen Performance Analysis an. Ich weiss nicht inwiefern es dir hilft, aber dort wird auch der Ram gemessen.
Zu den Referenzen: Auch Events und solches sind referenzen, also wenn du irgendwo ein Event hast, musst du das auch lösen.
Wie hast du deine Daten an dem ListView gebunden?Grüsse
Chiller