Endlos Backgroundworker memory problem
-
Ok, nun versperrt mit der Baum die Sicht auf den Wald.
Wo hast Du das mit dem "Runtime-Thread" her ? Kann ich nicht finden. Das was Du schreibst ist der Bereich, der die Finalizer aufruft, nicht der, der die Objekte in die Queue für den Finalizer Thread einreiht.
(collected aber ALLE Threads)
Und darum geht es ja. Wenn das ganze Global ist, müsste er alle Treads aller .Net Anwendungen die gerade laufen unterbrechen.
Das "Global arbeiten" hat aber auch Auswirkungen auf das "Timer -> 48h warten -> kein Collect"
(Ich denke mal, in diesem Punkt wird man nie auf ein Ergebnis kommen. Das dürfte sowas von Implementierungsabhängig sein - leider mit vielen direkten Auswirkungen.)
Naja, zum Spekulieren muss man nicht immer an die Börse, dafür gibt es hier keine Steuern
Das was Du zeigst ist der Workarround. Leider ist das Objekt, was im real code erzeugt wird Teuer. Und ich hätte es gerne pro Instanz nur einmal angelegt.
-
Knuddlbaer schrieb:
Wo hast Du das mit dem "Runtime-Thread" her ? Kann ich nicht finden. Das was Du schreibst ist der Bereich, der die Finalizer aufruft, nicht der, der die Objekte in die Queue für den Finalizer Thread einreiht.
J. Richter - Garbage Collection: Automatic Memory Management in the .NET Framework schrieb:
There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method.
http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/1100/gci/toc.asp
(collected aber ALLE Threads)
Ich meinte hier alle Threads einer laufenden Anwendung.
Das was Du zeigst ist der Workarround. Leider ist das Objekt, was im real code erzeugt wird Teuer. Und ich hätte es gerne pro Instanz nur einmal angelegt.
Wer hindert Dich daran?
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SmartDeviceProject2 { public partial class Form1 : Form { Graphics gr; test te = null; public Form1() { InitializeComponent(); te = new test(ref gr); } private void button1_Click(object sender, EventArgs e) { te.Main(); } } public class test { Graphics gr; public test(ref Graphics Gr) { this.gr = Gr; } public void Main() { if (gr == null) gr = Graphics.FromImage(new Bitmap(1, 1)); gr.MeasureString("Hallo Welt", new Font("Arial", 12, FontStyle.Regular)); } } }
Oder die "kurze" Lösung:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SmartDeviceProject2 { public partial class Form1 : Form { test te = null; public Form1() { InitializeComponent(); te = new test(); } private void button1_Click(object sender, EventArgs e) { te.Main(); } } public class test { Graphics gr; public void Main() { if (gr == null) gr = Graphics.FromImage(new Bitmap(1, 1)); gr.MeasureString("Hallo Welt", new Font("Arial", 12, FontStyle.Regular)); GC.KeepAlive(gr); } }
Oder die "saubere" Lösung:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SmartDeviceProject2 { public partial class Form1 : Form { test te = null; public Form1() { InitializeComponent(); te = new test(); } private void button1_Click(object sender, EventArgs e) { te.Main(); } } public class test : IDisposable { Graphics gr; public void Main() { if (gr == null) gr = Graphics.FromImage(new Bitmap(1, 1)); gr.MeasureString("Hallo Welt", new Font("Arial", 12, FontStyle.Regular)); } #region IDisposable Member public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (gr != null) gr.Dispose(); } } #endregion } }
J.Richter in Cwalina & al. - Framework Design Guidelines, S. 253 schrieb:
The idea here is that Dispose(Boolean) knows whether it is being called to do explicit cleanup (the Boolean is true) versus being called due to a garbage collection (the Boolean is false). This distinction is useful because, when being disposed explicitly, the Dispose(Boolean) method can safely execute code using reference type fields that refer to other objects knowing for sure that these other objects have not been finalized. When the boolean is false, the Dispose(Boolean) method should not execute code that refers to reference type fields because those objects might have already been finalized.
Hoffe, dass Dir das weiterhilft.
-
Na, dann aber bitte richtig lesen
There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method.
Es geht hier um den Thread, der die Objekte in die freachable Queue steckt.
[quote]
(collected aber ALLE Threads)Ich meinte hier alle Threads einer laufenden Anwendung. [quote]
Das steht aber ein wenig mit den anderen Aussagen in Konflikt: Systemweiter Heap der Komprimiert wird. (Aber hier wird der SSCLI Code vermutlich helfen, es wird nur ne weile dauern bis ich den durch hab.)
Das SupressFinalize ist eine nette Idee, funktioniert leider nicht.
http://www.rothmichael.de/cpp/GC-CF20.swf (CF20 SP1)Unter CF3.5 geht es wie erwartet. Also muss ich mich nicht mehr so lange mit den "Merkwürdigkeiten" beschäftigen.
Btw.: Was bei dem Problem noch hilft das das Pinnen des Objektes (die Variante die noch verschoben werden darf.)
-
Knuddlbaer schrieb:
Na, dann aber bitte richtig lesen
Yepp. Da hab ich mich wohl falsch ausgedrückt, gemeint war die freachable queue. Wenn Du Informationen über einen separaten Thread hast, "der die Objekte in die freachable Queue steckt", nur her damit...! Mein Wissen ist z.Z. dass dies innerhalb des ge-hijackten und suspendierten Threads geschieht.
Das SupressFinalize ist eine nette Idee, funktioniert leider nicht.
http://www.rothmichael.de/cpp/GC-CF20.swf (CF20 SP1)Lass mal GC.Collect() weg, dann wird's schon tun. Hab ich da "Danke" gehört?
Und falls Du mit GC.Collect() einen GC-Lauf nur als Test für die Solidität Deiner Klasse erzwingen wolltest (Konstruktion ist teuer usw.), dann solltest Du lieber die Klasse vorher abändern:
public class test { Graphics gr = null; Bitmap bm = null; public void Main() { if (gr == null) { if (bm == null) bm = new Bitmap(1, 1); gr = Graphics.FromImage(bm); } gr.MeasureString("Hallo Welt", new Font("Arial", 12, FontStyle.Regular)); GC.Collect(); } }
Na sowas...
-
Moins,
Wenn Du Informationen über einen separaten Thread hast, "der die Objekte in die freachable Queue steckt", nur her damit...! Mein Wissen ist z.Z. dass dies innerhalb des ge-hijackten und suspendierten Threads geschieht.
Naja, das ergibt IMHO aus
Eine Garbage Collection kann durch die Common Language Runtime (CLR) gleichzeitig in einem separaten Thread oder im selben Thread wie die Anwendung ausgeführt werden.
http://msdn2.microsoft.com/de-de/library/at1stbec(VS.80).aspx schrieb:
Eine Garbage Collection kann durch die Common Language Runtime (CLR) gleichzeitig in einem separaten Thread oder im selben Thread wie die Anwendung ausgeführt werden. Verwenden Sie das <gcConcurrent>-Element, um festzulegen, wie die Garbage Collection ausgeführt werden soll. Im Folgenden wird veranschaulicht, wie gleichzeitige Garbage Collection-Vorgänge deaktiviert werden.
Ich muss hier jetzt aber ehrlich gestehen, das ich erst einmal eine Auszeit benötige um das innerhalb diesen Threads erlange Wissen zu Reflektieren. Betreffend des GC ist die Aufnahmekapazität meinerseits erst einmal erschöpft, ich muss erst mal das neue Wissen unter ein Dach bekommen und dann das ganze mit dem neuen Wissen betrachten.
CF.Net 2.0 GC
Das GC.Collect wird an der Stelle zwar erzwungen, kann jedoch jederzeit durch das System ausgelöst werden. (z.B. die 1MB Grenze). Ist bin auf die Problematik gestoßen weil beim Testen der Komponente alles lief, in einem großen Projekt es aber plötzlich eine Disposed Exception geflogen ist.
Das letzte Beispiel hat aber den entscheidenden Unterschied gebracht:
gr = Graphics.FromImage(new Bitmap(1,1));
gr = Graphics.FromImage(bm);
Und schon fühlt man sich, als wäre man gegen die Wand gerannt.
Da die Implementierung von Graphics unklar ist, war die Annahme, das Graphics das Bitmap Referenziert falsch.
gr = Graphics.FromImage(new Bitmap(1,1));
übergibt eine Temporäre Variable. Ausgehend von dem was man in C++ in dieser Situation gelernt hat, müsste man im Übertragenen Sinn davon ausgehen, das die unbenannte Variable nach dem Methodenaufruf nicht mehr Referenziert ist und somit natürlich aufgeräumt werden darf. (Warum das unter CF 3.5 jetzt klappte sei einfach mal als Zufall bzw. undefiniert hingestellt.)
[edit]Das ist jetzt Spekulation, erscheint mir aber sehr schlüssig.[/edit]
Somit kann ich nun mit einer Erleuchtung an die dunklen Stellen des Codes gehen und ganz entspannt diese Erkenntnis auf die verwendeten Objekte übertragen und das neue Wissen wirken lassen.
Hab ich da "Danke" gehört?
Ahem... Hab ich was verpasst ? Ich entnehme dem mal, das ich Undankbar gewirkt habe ?
Dies ist aber nicht der Fall. Die Diskussion ist zwar nicht in allen Ebenen auf ein abschließendes Ergebnis gekommen (aber auch noch nicht gänzlich beendet, aber erst mal Pause), hat aber sehr viel Wissen vermittelt. Alleine die Tatsache, der Diskussion auf dieser Ebene beigewohnt zu haben sorgte dafür, das man sich beim Zusammensuchen der Belege für das Wissen was man zu diesem Zeitpunkt hatte, in ein neues Licht rückte. Die Gegenargumente haben einen die Informationen, die man bereits verinnerlicht hat aufgerüttelt so das man diese erneut aus einer anderen Perspektive untersucht hat und nicht als "bereits gelesen" abgehakt hat.
Ich habe zumindest für meinen Teil viele Informationen gesammelt und hierfür sei Dir meine Dankbarkeit gewiss, schließlich hast auch Du viel Zeit Investiert (und hoffentlich ebenso Wissen aus der Diskussion entnehmen können.)
Ich hoffe doch sehr, das wir das Thema GC zu einem späteren Zeitpunkt fortsetzen. In dieser Zwischenzeit möchte ich mich noch einmal intensiv mit dem Thema beschäftigen und hierbei das hier erworbene Wissen einfließen lassen. Natürlich werde ich den Thread nicht ignorieren und bei Fragen weiterhin mit Diskutieren
Vielen Dank!
-
http://msdn2.microsoft.com/de-de/library/at1stbec(VS.80).aspx schrieb:
Eine Garbage Collection kann durch die Common Language Runtime (CLR) gleichzeitig in einem separaten Thread oder im selben Thread wie die Anwendung ausgeführt werden. Verwenden Sie das <gcConcurrent>-Element, um festzulegen, wie die Garbage Collection ausgeführt werden soll. Im Folgenden wird veranschaulicht, wie gleichzeitige Garbage Collection-Vorgänge deaktiviert werden.
Die GC-Thematik ist äußerst umfangreich und ungewöhnlich lückenhaft dokumentiert. Die wenigen Brocken, die man (wie ich) hier und da gesammelt hat, entbeeren in den meisten Fällen einer soliden Grundlage. Microsoft hat erst seit kurzem, mit der Veröffentlichung des Shared-Source-Quellcodes zum .NET-Framework, einen Riesen-Schritt in Richtung Aufklärung gemacht. Ich erwarte deswegen, dass die Forschung auch im Punkt GC bald weiterkommt und den GC aus der "wird schon tun oder so"-Blackbox befreit und ans Licht bringt. Wie Deine Zitate belegen, Knuddlbaer, geht es im zitierten Text um die CLR und nicht direkt um den GC, sondern um das Collecten von Garbage, ein feiner Unterschied. Denn, wie es scheint, ist der GC nur Teil eines größeren "Puzzles", der dem Aufräumen von Garbage dient. Aber, wie Du schon in weiser Voraussicht sagst: Warten wir's ab, es kann nicht mehr lange dauern bis die Erkenntnisse vorliegen. "Denn unser Wissen ist Stückwerk, und unser Weissagen ist Stückwerk."
Knuddlbaer schrieb:
Das GC.Collect wird an der Stelle zwar erzwungen, kann jedoch jederzeit durch das System ausgelöst werden. (z.B. die 1MB Grenze).
Ich habe leider erst spät bemerkt, was Du mit GC.Collect() an jener ungewöhnlichen Stelle simulieren wolltest. Hätte ich gleich checken müssen. Du hattest ja schon vorher geschrieben, wie "teuer" das Erstellen des Objekts sei. Aber GC.Collect war nun einmal nicht in meinem Code drin, und ohne GC.Collect funktionierte alles wunderbar... Erst als ich mich nach dem Sinn dieser Stelle fragte und sah wie Bitmap und der Font erzeugt wurden, ging mir ein Licht auf.
Knuddlbaer schrieb:
Und schon fühlt man sich, als wäre man gegen die Wand gerannt.
Ging mir nicht anders.
Knuddlbaer schrieb:
Warum das unter CF 3.5 jetzt klappte sei einfach mal als Zufall bzw. undefiniert hingestellt.
Vielleicht hängt es mit den neuen, anonymen Typen in C# 3.0 zusammen (Spekulation)?
Ich entnehme dem mal, das ich Undankbar gewirkt habe ?
Sorry, Knuddlbaer, das wollte ich nicht sagen! Ich meinte nur, dass mein Code auch auf dem CF 2.0 wunderbar funktionierte: Wenn man eine einzige Zeile Code (Dein Code) weglies. Das war eine blöde Art zu sagen, guck mal wie einfach das ist. Nein, ich habe in diesem Thread keine Zeit verloren, und Dein Pathos stand meinem in nichts nach. Was, übrigens, auch über den Wissensstand gesagt werden kann. Ich bin glücklich darüber, in diesem Forum Leute gefunden zu haben, mit denen man sich so ausführlich austauschen kann.
Ich danke Dir für diese sehr ergiebige Diskussion!
-
Jup, hoffe auch das man bald mehr Infos dazu bekommt
Blackbox und Hellsehen passen so gut zusammen wie dunkel und weiß
-
Der Artikel ist zwar schon etwas älter (2004), aber gibt einies Tipps zum Thema GC:
-
loks schrieb:
Der Artikel ist zwar schon etwas älter (2004), aber gibt einies Tipps zum Thema GC: http://msdn2.microsoft.com/de-de/library/ms973837.aspx
Es gibt viele solche Artikel. Und sie sind wirklich gut. Aber die GC-Implementierungsdetails die auch Knuddlbaer vermisste, fehlen überall.
Die meisten GC-Artikel sind aus der Vogelperspektive geschrieben und enthalten Warnungen vom Typ "Note that this is not what is actually implemented" (Rico Mariani). "Was die Welt im Innersten zusammenhält", verraten diese Artikel leider nicht.
-
mammamia;