errox Geschrieben 24. Oktober 2011 Geschrieben 24. Oktober 2011 Hallo Freunde, und wieder ein Problem. Also ich schilder es euch kurz mal: Ich hab ein Panel in dem ich Buttons ausrichte. Die immer wieder neu erzeugt werden (temporär). Dabei ist mir was "tolles" aufgefallen Code: System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); foreach (Button btn in panel1.Controls) { btn.Dispose(); } panel1.Controls.Clear(); List<Button> list = new List<Button>(); int size = 50; for (int i = 0; i < 60; i++) { for (int ii = 0; ii < 7; ii++) { Button btn = new Button(); btn.Location = new Point(ii * size, i * size); btn.Size = new Size(size, size); btn.Text = "A"; list.Add(btn); } } panel1.Controls.AddRange(list.ToArray()); sw.Stop(); lblDuration.Text += sw.ElapsedMilliseconds + "\r\n"; Man braucht immer länger, bis sich die Liste Initialisiert hat. ABschnitt von lblDuration: 197 590 ... 652 ... 768 794 ... 877 ... ... 1025 Das beste, nach einer bestimmten Zeit krieg ich bei AddRange eine Win32 Exception (Fehler beim Erstellen des Fensterhandles.) Warum dauert es immer länger? ich bereinige doch die Ressoucen mit Dispose und Clear es vorher sogar. Wisst ihr ne lösung? Gruß und Danke! Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Warum erstellst du die Buttons überhaupt immer wieder neu? Bist du sicher, dass du nicht noch irgendwo eine Referenz auf die Buttons hast? Zitieren
lilith2k3 Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Warum dauert es immer länger? ich bereinige doch die Ressoucen mit Dispose und Clear es vorher sogar. Was glaubst Du eigentlich geschieht, wenn Du die Dispose-Methode eines Objektes aufrufst? Überlege: 1) Dispose ist kein Destruktor 2) .NET ist eine managed Umgebung 3) C# ist nicht C++ Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 Also das mit dem jedesmal neu erzeugen ist eigentlich nur tempoär. Aber trotzdem regt mich das auf ich will wissen, wie ich dieses problem (addrange wird langsamer / win32 exception) lösen kann. Ich kann mir das nicht erklären. Ihr euch?? Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 ich will wissen, wie ich dieses problem (addrange wird langsamer / win32 exception) lösen kann.Vermutlich gehen Windows irgendwann die Fensterhandles aus. Wie oft wird dieser Code denn ausgeführt? Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 GRade zu Demozwecken 420 (7 * 60) mal. Jedoch stürzt er vorher schon ab. Diese Handels, die zuvor verwendet wurden, wie kann ich die bereinigen? Zitieren
Gateway_man Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt. Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers(). lg Gateway Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 GRade zu Demozwecken 420 (7 * 60) mal.Ich meinte, wie oft der ganze oben gezeigte Code ausgeführt wird. Dass du da 7 * 60 Buttons erstellst, sehe ich selbst. Aber wie oft passiert das? Ins Verhalten des GC solltest du nur dann eingreifen, wenn du ganz genau weißt, was du tust, und wenn du sicher bist, dass du das Problem nicht anders lösen kannst. Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt. Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers(). lg Gateway Ich werd da mal was Probieren, Danke! Ich meinte, wie oft der ganze oben gezeigte Code ausgeführt wird. Dass du da 7 * 60 Buttons erstellst, sehe ich selbst. Aber wie oft passiert das? Ins Verhalten des GC solltest du nur dann eingreifen, wenn du ganz genau weißt, was du tust, und wenn du sicher bist, dass du das Problem nicht anders lösen kannst. 1 Mal drück ich auf den Button. Das mit dem Clear / AddRange läuft aber vllt max. 30 mal durch. Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Ich werd da mal was Probieren, Danke!Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen. 1 Mal drück ich auf den Button. Das mit dem Clear / AddRange läuft aber vllt max. 30 mal durch.Ist da noch eine Schleife drumherum, oder warum löst ein einziger Klick aus, dass das 30 Mal ausgeführt wird? Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 und wofür? "Kassensystem" Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen. Wie dann? :confused: Ich mach doch alles richtig, oder nicht? Ist da noch eine Schleife drumherum, oder warum löst ein einziger Klick aus, dass das 30 Mal ausgeführt wird? Nein, nur die Methodendeklaration. Das ist der grund, dass es beim "Neuinitialisieren" die Buttons vom Panel alle entfernt werden, und wieder neu eingefügt. buttons können neu hinzugefügt werden, das Panel kann gecleared werden und die Buttons ausgerichet, etc. Das ist nur die "basis" um zu testen wo das Problem liegt. Besser als würd ich das von hand machen Zitieren
SilentDemise Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 ich bezweifel ehrlich gesagt, dass dein Ansatz der richtige für was auch immer für ein Problem ist. Irgendwelche Controls andauernd neuzuinitialisieren scheint mir kein guter Ansatz zu sein. Zitieren
Gateway_man Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 (bearbeitet) Bitte nicht versuchen, solche Probleme durch Rumfummeln am GC zu lösen. In den allerallermeisten Fällen sollte man von GC.Collect die Finger lassen. Was heißt hier Rumfummeln . Die Funktionen sind in keinster weise schädlich. Dem GC wird nur ein kleiner schubser gegeben. Frei nach dem Motto: "Beweg dein hintern es gibt was zu tun". Wir nutzten hier die Funktion eigentlich regelmäßig (müssen es sogar), da der GC scheinbar etwas langsam zu potte kommt. Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wir Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden. Das Problem ist nur das es ewig dauert bis Sie dann auch wirklich freigegen sind. Deswegen ==> GC.Collect() Also ich würd die Funktion nicht so verteufeln. lg Gateway Bearbeitet 25. Oktober 2011 von Gateway_man Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Ich mach doch alles richtig, oder nicht?Keine Ahnung, dafür hast du nicht genug Informationen geliefert. Es ist immer noch nicht klar, in welchem Kontext dein Code steht. Nein, nur die Methodendeklaration.Ich verstehe nur Bahnhof. Erklär mir bitte, wie ein einzelner Klick dazu führen kann, dass dieser Code 30 Mal ausgeführt wird. Das ist nur die "basis" um zu testen wo das Problem liegt.Was genau ist denn das eigentliche Problem, das dich dazu gebracht hat, diesen Testcode zu erstellen? Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Was heißt hier Rumfummeln . Die Funktionen sind in keinster weise schädlich.Ist dir das Generationenkonzept des GC bekannt? Jeder Aufruf von Collect bewirkt, dass nicht freigegebene Objekte in eine höhere Generation kommen. Ein Aufruf von Collect zum falschen Zeitpunkt kann sich nachteilig auf Performance und Speicherverbrauch auswirken. Außerdem pfuscht man damit im Selbst-Tuning des GC herum. Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wird Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden.Es gibt Fälle, in denen die Verwendung von GC.Collect angebracht ist. Aber keinesfalls sollte das eine allgemeine Empfehlung zum Lösen von Speicherproblemen sein. Also ich würd die Funktion nicht so verteufeln.Ich schon. Nur wenn man genau weiß, wie der GC arbeitet, sollte man ihm ins Handwerk pfuschen. Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 Okey. Ich versuch mal die funktion zu erklären. Vllt wirds einem klar. Wenn fragen sind, melden. Ich hab ein programm. Soweit so gut In dem programm ist ein panel. In dem programm kann man eine anzahl eingeben, wieviele buttons in das panel hinzugefügt werden sollen. Diese buttons können durch eine klasse verschoben werden (das ist eine andere geschichte). Ausserdem kann ich durch ein button das panel leeren. Funktion: kassensystem und patchworkart für meine nichten. Soweit sogut. Dabei ist mir aufgefallen, wenn ich jedesmal 500 buttons neu hinzufüg und lösch, gibts probleme. Aber erst wenn ich das einigemale mach. Alternative: neu ausrichten. Nicht möglich, da ich vllt von 500 auf 10 buttons will, und dann wiedet auf 400, etc. Also der lösungsansatz controls neu hinzufügen / entfernen bleibt. Weil ich möchte jetzt auch selber wissen wie ich das beheben kann. Deswegen hab ich eine form Zum testen gemacht, die nix anderes tut als buttons hinzufügen und entfernen, um der sache auf den grund zu gehen. Ohne click events, erc. Jedoch komme ich hier nicht weiter, da ich nicht weiss wie ich was und wie aufrufen soll, damit ich keine win32 exception bekomm beim addrange und das nicht jedesmal länger dauert. Ich hoff alle fragen sind geklärt Danke für eure hilfe!!! Zitieren
Klotzkopp Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Dabei ist mir aufgefallen, wenn ich jedesmal 500 buttons neu hinzufüg und lösch, gibts probleme.An dieser Stelle wären jetzt mehr Details toll gewesen. Mit "es gibt Probleme" kann man so gar nichts anfangen. Ein GUI mit 500 Buttons ist in meinen Augen unbedienbar. Tritt das Problem auch bei "normaler" Verwendung auf? Deswegen hab ich eine form Zum testen gemacht, die nix anderes tut als buttons hinzufügen und entfernen, um der sache auf den grund zu gehen. Ohne click events, erc. Jedoch komme ich hier nicht weiter, da ich nicht weiss wie ich was und wie aufrufen soll, damit ich keine win32 exception bekomm beim addrange und das nicht jedesmal länger dauert. Grob gesagt ist jeder Prozess auf 10.000 Fenster beschränkt. Wenn der GC in deinem Fall nicht dazu kommt, die alten abzuräumen, wird dein Prozess irgendwann keine neuen Fenster mehr erstellen können. Das dürfte der Grund für die Exception sein. Es kann sein, dass du das durch Erzwingen der Garbage Collection beheben kannst. Solange es nur dazu dient, in einem Testprogramm ein anderes Problem reproduzierbar zu machen, kann man das vertreten. Zitieren
lilith2k3 Geschrieben 25. Oktober 2011 Geschrieben 25. Oktober 2011 Nur weil man im .NET ein Dispose absetzt, heißt das noch lange nicht, das der GC das Element auch zur selben Zeit aus dem Speicher entfernt. Du könntest einfach mal ein GC.Collect() absetzten und den GC somit zwingen nicht verwendeten Speicher freizugeben. Oder alternativ GC.WaitForPendingFinalizers() GC.Collect() heißt nicht, dass ein Collect initiiert wird; sondern metaphorisch gesehen schwenkst Du ein rotes Fähnchen mit dem Du signalisierst, dass der GC sich eventuell ein wenig beeilen sollte, einen Collect durchzuführen. Die Funktionen sind in keinster weise schädlich. Dem GC wird nur ein kleiner schubser gegeben. Frei nach dem Motto: "Beweg dein hintern es gibt was zu tun". Wie schon dargelegt worden ist, ist es in den meisten Fällen eher kontraproduktiv. Wir nutzten hier die Funktion eigentlich regelmäßig (müssen es sogar), da der GC scheinbar etwas langsam zu potte kommt. Wenn die Funktion nicht wäre hätte ich des öffteren OutOfMemory Exceptions und das obwohl die Kiste laut TaskManager noch 1.5 GB freien Ram hat. Und ja wir Zerstören eigentlich die großen Datenkonstrukte, wenn Sie nicht mehr gebraucht werden. Das Problem ist nur das es ewig dauert bis Sie dann auch wirklich freigegen sind. Deswegen ==> GC.Collect() Ich würde mir an der Stelle Gedanken um die Programmierung machen. Nochmal: C# ist kein C++ und managed heißt eben managed. Wer das nicht will, sollte über die Wahl seines Werkzeuges nachdenken. Einzig sinnvolles Einsatz-Szenario von Dispose() ist in eigenen Objekten, welche selbst mit unmanaged code angereichert sind, dass in dem Fall Speicher freigegeben wird. Zum Thema GC.Collect() Rule #1 Don't. This is really the most important rule. It's fair to say that most usages of GC.Collect() are a bad idea and I went into that in some detail in the orginal posting so I won't repeat all that here. @TO Ich bezweifele gerade, dass Du Dir Gedanken um eine Lösung gemacht hast. Auch ohne im Detail zu wissen, resp. verstanden zuhaben, was Du vorhast, hört sich Deine Lösung recht unausgegoren an. Zitieren
errox Geschrieben 25. Oktober 2011 Autor Geschrieben 25. Oktober 2011 Ich glaube das ist für ALLE interessant: foreach (Button btn in panel1.Controls) { btn.Dispose(); } Disposed nur jedes zweite objekt.... Disposed jedes: int count = pnlItems.Controls.Count; for (int i = 0; i < count; i++) { pnlItems.Controls[0].Dispose(); } Zitieren
Pointerman Geschrieben 26. Oktober 2011 Geschrieben 26. Oktober 2011 Moin! Ich glaube Deine Verwunderung bei der foreach-Schleife liegt daran, dass Du eine wichtige Regel der foreach-Schleife nicht verinnerlicht hast: Sie sollte nicht eingesetzt werden, um die Inhalte der Auflistung zu ändern, sodass unvorhersehbare Nebeneffekte vermieden werden. Nachzulesen hier: Über foreach bei MSDN Und Du hast recht, dass ist für alle interessant und diese Regel sollte einem in Mark und Bein übergehen. Zitieren
SilentDemise Geschrieben 26. Oktober 2011 Geschrieben 26. Oktober 2011 Dispose aufruf via foreach fällt aber nicht unter die Regel, Methodenaufrufe sind völlig i.O. Zitieren
Sacaldur Geschrieben 26. Oktober 2011 Geschrieben 26. Oktober 2011 in deinem Kassensystem fügst du dynamisch sehr viele Buttons hinzu wofür? was machen die? warum müssen es viele sein? ich denke, das ist dein Problem: du brauchst diese vielen Buttons eigentlich gar nicht, verwendest sie aber dennoch was ist überhaupt mit "Kassensystem" gemeint? was macht dein Programm? was macht der Benutzer mit deinem Programm? es fehlen immernoch Details, ohne die dir keine richtig helfen kann viele versuchen es zwar dennoch, aber da Details fehlen, kommt es zu keinem Ergebnis Zitieren
Pointerman Geschrieben 26. Oktober 2011 Geschrieben 26. Oktober 2011 @SilentDemise Dispose aufruf via foreach fällt aber nicht unter die Regel, Methodenaufrufe sind völlig i.O. Hast Du naehere Informationen dazu bzw einer Erklaerung dafuer, warum der Dispose()-Aufruf ausgenommen ist? Ich haette jetzt ehrlich gesagt vermutet, dass durch das Dispose() die Auflistung geaendert wird. Konnte bei einer google-Suche nichts dazu finden. Zitieren
Klotzkopp Geschrieben 26. Oktober 2011 Geschrieben 26. Oktober 2011 (bearbeitet) Es ist wohl tatsächlich so, dass Control.Dispose das Steuerelement aus der Controls-Collection seines Parent-Controls entfernt. Siehe dazu: How to: Add to or Remove from a Collection of Controls at Run Time -> Erst Remove, dann Dispose Bearbeitet 26. Oktober 2011 von Klotzkopp Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.