Endlos Backgroundworker memory problem
-
Ja, das ist ein P/Invoke, ein API Aufruf. Das ist keine Visual Basic Funktion.
-
und was soll man jetzt benutzen ?
und soll man doch den GC aufrufen oder nicht ?
(habe as mal ohne die sleeps am laufen und da läuft´s auf nem bestimmten resourcenlevel durch, ohne ständig anzuwachsen)
CU
-
http://www.amazon.de/Microsoft-NET-Framework-Programmierung-Expertenwissen-Framework/dp/3860639846/ref=sr_1_1?ie=UTF8&s=books&qid=1205407278&sr=8-1
Hier findet ihr detailiertes Wissen über den GC (allerdings nicht explizit für die CF Version).
Mir persönlich gefällt das Buch sehr gut.
Simon
-
Ähmm ich will mir jetzt kein buch deswegen kaufen !
arbeite eh schon wieder an was anderem und das in c++.Gibt´s keine einfache antwort auf meine fragen ?
THANX
CU
-
@simon Wenn Du die Beiträge aufmerksam liest, wird Dir sicherlich auffallen, das aus diesem Buch zitiert wurde. Wenn Du dann noch das Buch liest, wirst vermutlich auch Du die Antworten vermissen. Dennoch vielen Dank für den Hinweis - das Buch kann man wirklich Empfehlen.
Verwende Thread.Sleep. Unterm Strich wird da vermutlich auch nur ein Aufruf auf die WinAPI Funktion Sleep sein, jedoch kann sich das jederzeit ändern. Thread.Sleep garantiert z.B., das dies auch mit Mono lauffähig ist.
WinAPI Aufrufe aus .Net schiebt man so lange wie es geht. An sich nutzt man diese nur, wenn das Framework einen keine Möglichkeiten bietet das Problem zu lösen. Thread.Sleep ist aber eine sehr gute Lösung
Den Garbage Collector solltest Du aufgrund des langen Blockieren des Threads einmal aufrufen. Da nicht sicher gestellt werden kann, wie der GC mit Deinem Prozess verknüpft ist, kann im schlimmsten Fall das von maro beschriebene Problem eintreten: Der GC gibt den Speicher nicht frei.
Für Details sind Deine Informationen unzureichend so das man von dem allgemeinsten Fall ausgehen muss. (Unbekannte CPU, Workstation und eine Konfiguration für den Prozess die den GC in den Anwendungsthread presst. Und letzteres ist das Problem: Läuft der GC im Anwendungsthread wird auch dieser durch ein Sleep blockiert. Im Normalfall wird der GC aber in einem Thread parallel zur Anwendung geführt, mit niedriger Priorität.)
-
Knuddlbaer schrieb:
Das Problem des nicht Verstehens löse ich aber auch nicht mit ner Combobox
OK, Knuddlbaer. Hier sind meine "2 Cents":
1. In welchem Thread läuft der GC ? (Vorallem was das Compact Framework betrifft)
Es gibt keinen speziellen Thread für das Collecten von Objekten. Sobald der Code einen "safe point" erreicht, wird die Ausführung des Codes soz. gekappert. Alle Threads bis auf dem Thread mit dem "safe-point" werden suspendiert und der GC setzt seine Arbeit im Thread mit dem "safe-point" fort (collected aber ALLE Threads).
Details: Beim Kompilieren der Anwendung werden sogenannte "safe points" für eine sichere Thread-Suspendierung im Code einefügt. Es sind dies Punkte wie z.B. die Rückkehr aus einem Funktions-Aufruf. Der GC hijackt den Ausführungspfad an diesen Safepoints, indem er eine andere Rücksprungadresse vom safe-point einfügt. Unter dieser Adresse befindet sich ein Aufruf der die aktuellen Threads unterbricht. Nach getaner Arbeit (Graph-Erstellung und Markierung, Kompaktierung usw.) wird wieder an die ursprüngliche Adresse gesprungen, die Thread-Suspendierung wird aufgehoben und der GC fährt mit dem Abarbeiten der finalizatin queue in einem separaten Runtime-Thread fort, während die Anwendung ihrer Wege geht.
2. Ist der Heap pro Appilkation oder Systemweit?
Der "managed heap" ist systemweit.2.1. Wie stellt der GC sicher, das es wegen Fragmentierung zur Speicherknappheit kommt.
Indem er den Heap ständig kompaktet.Details: Wenn ein Prozess gestartet wird, reserviert die CLR einen kontinuierlichen, virtuellen Adressraum, dem noch kein realer Speicher zugewiesen ist (über VirtualAlloc). Ein NextObjPtr verweist am Anfang auf den Beginn des Adressraumens. Wenn ein neues Objekt auf dem Heap erstellt werden soll, wird die Größe des Objekts zur aktuellen Adresse des NextObjPtr dazuaddiert. Wenn der Heap feststellt, dass NextObjPtr außerhalb des reservierten Adressraums zeigt, ist der Heap voll und der GC muss collecten.
3. Kann mir das verhalten der CF Anwendung im vollen Framework auch passieren ?
Ich verstehe noch nicht ganz, was auf Deinem Gerät passiert. Es gibt eine Reihe von Unterschiede zw. CF/GC und dem "normalen" GC: Der CF-GC verwendet soviel ich weiß keine Generationen-Optimierung, auch werden große Objekte nicht anders als kleine behandelt. Im CF gibt es keine Workstation/Server-GC-Modelle. Und: Der CF-GC kann u.U. ge-jitteted Code sofort nach der Ausführung freigeben (code pitching).
"Developers interested in the internal workings of the .NET Framework can explore this implementation of the CLI to see how garbage collection works, JIT compilation and verification is handled, security protocols implemented, and the organization of frameworks and virtual object systems."
-
Die Cents sind in der falschen Währung.
Zu 1:
ine Garbage Collection kann durch die Common Language Runtime (CLR) gleichzeitig in einem separaten Thread oder im selben Thread wie die Anwendung ausgeführt werden
Das hat mit den Safepoints erst mal nichts zu tun. Wie MS selbst angibt, kann der GC diese Arbeit auch im Anwendungsthread machen. Der müsste dafür ja dann doch aber Idlen - wenn er Blockiert wird (z.B. WaitHandle) dürfte auch der GC Blockiert sein.
Zu 2:
Wenn der Heap Systemweit komprimiert wird, würde dies bedeuten, das die Threads aller .Net Anwendungen angehalten werden müssen. Ich würde dann auch keinen Sinn darin sehen, das der GC in den Anwendungsthread gepackt werden kann wenn doch ohnehin irgendwo ein globaler GC überwachen muss.
Denkbar ist, das der Heap Systemweit ist , jede Anwendung ein Stück des Heaps zugewiesen und reserviert bekommt und global über die Wurzel verwaltet wird. Dazu wäre dann aber auch ein Systemweiter GC nötig ?
[ großer globaler Heap von dem jeder etwas abbekommt] [Anwendung A][Anwendung B][Anwendung C][Anwendung D]
So, nun braucht Anwendung A mehr Platz, B und C D sind gut Optimiert. Was passiert nun ?
[ großer globaler Heap von dem jeder etwas abbekommt ] [Anwendung B][Anwendung C][Anwendung D][Anwendung A ]
Was passiert wenn C komplett wegfällt ?
[ großer globaler Heap von dem jeder etwas abbekommt ] [Anwendung B] [Anwendung D][Anwendung A ]
Somit wäre der Heap Fragmentiert, A D und B für sich vllt. nicht. Damit nun der gesamte Heap defragmentiert werden kann müssen alle .Net Threads angehalten werden. Besonders teuer wären dann .Net Anwendungen die P/Invoke aufrufe machen, die können im Nativecode nicht angehalten werden.
Und hier fehlt mir jetzt in der Dokumentation der Hinweis, was genau passiert. Werden wirklich alle .Net Threads Systemweit angehalten ? (Ich wüsste nicht, wie der GC diesen sonst komprimieren soll.)
Zu 3.
Was auf dem Gerät passiert kannst Du ausprobieren - der Emulator geht. Wenn Du Dir das Beisiel anschaust. Was würdest Du sagen - beim 2. Klick auf den Button eine Exception oder keine ?Thx für den Link, wird mich eine weile beschäftigen.
-
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
Lies doch mal bitte den ersten Punkt bis ans Ende ("...in einem separaten Runtime-Thread").
Das hat mit den Safepoints erst mal nichts zu tun. Wie MS selbst angibt, kann der GC diese Arbeit auch im Anwendungsthread machen. Der müsste dafür ja dann doch aber Idlen - wenn er Blockiert wird (z.B. WaitHandle) dürfte auch der GC Blockiert sein.
Hmmm... Ich würde mich fragen: Woher weiss der GC, wann es die Ausführung eines Threads gefahrlos unterbrechen kann?
Zu 2:
Das ist reine Spekulation (auch meinerseits).
Zu 3.
Was auf dem Gerät passiert kannst Du ausprobieren - der Emulator geht. Wenn Du Dir das Beisiel anschaust. Was würdest Du sagen - beim 2. Klick auf den Button eine Exception oder keine ?
Wie wär's denn mit:
public class test { public void Main() { Graphics gr = null; if (gr == null) gr = Graphics.FromImage(new Bitmap(1, 1)); gr.MeasureString("Hallo Welt", new Font("Arial", 12,FontStyle.Regular)); gr.Dispose(); GC.Collect(); // Aufräumen erzwingen. GC.WaitForPendingFinalizers(); GC.Collect(); } }
Ich guck mir Dein Beispiel noch an...
-
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;