Skillbyte Podcast #52: So funktioniert der Garbage Collector!

Skillbyte Podcast #52: So funktioniert der Garbage Collector!

Willkommen zum Skillbyte-Podcast! Skillbyte ist Ihr Partner für digitale Exzellenz.

In diesem Podcast geht es um das Thema: Digitale Bildung in Deutschland

// Inhalt //
01:55 - Historische Entwicklungen vor dem Garbage Collectors (GC)
06:55 - Der Aufstieg der Interpreter
11:20 - Der Garbage Collector übernimmt das Speichermanagement
14:33 - So arbeitet der Garbage Collector
15:39 - Tradeoff: Durchsatz vs. Latenz
20:02 - Garbage Collection im Detail
21:10 - Funktionsweise des Concurrent Mark Sweep Garbage Collectors
26:04 - Exkurs G1 Garbage First Collector
27:19 - Zusammenfassung
29:33 - Tuning

String Depublication: https://blog.codecentric.de/2014/08/string-deduplication-ein-neues-feature-java-8-update-20/

Abonnieren Sie diesen Podcast und besuchen Sie uns auf https://www.skillbyte.de

Feedback und Fragen gerne an podcast@skillbyte.de

Skillbyte Technologie Podcast · Podcast #52: So funktioniert der Garbage Collector!

AUTOMATISCH ERZEUGTES TRANSKRIPT

Herzlich Willkommen zur Skillbyte Podcast Episode Nr. 52 So funktioniert der Garbage Collector abonniert unseren Podcast für mehr spannende Themen aus dem Technologie im Feld, wenn ihr alte Entscheider oder IT-Fachkräfte seid. Wir freuen uns immer über Hörer. Fragen zur aktuellen Episode an Podcasts Skillbyte. Auch freuen wir uns über Bewertungen und ganz besonders an Weiterempfehlung an eure Freunde und Kollegen, die sich ebenfalls für Technologie interessieren. Heute möchte ich darüber sprechen, wie der Garbage Collector funktioniert und ein bisschen einordnen. Wo kommt der geschichtlich her?

Warum ist sehr wichtig. Und wie ist die Funktionsweise eines Garbage Collectors, der ja in modernen Programmiersprachen fast immer angewendet wird, um eben die Softwareentwicklung zu vereinfachen? Ich glaube, es ist ein sehr spannendes Thema, auch wenn es sehr technisch ist und einige Details zutage fördern wird. Ich bemühe mich, dies so plastisch wie möglich zu veranschaulichen. Der Garbage Collector ist eine Technologie, der sich in modernen Programmiersprachen und Laufzeit Umgebungen weitestgehend durchgesetzt hat. Da ist es recht relevant zu verstehen, wie er funktioniert.

Wenn eine Anwendung zum Beispiel Probleme bereitet und man verstehen möchte, warum hängt es vielleicht mit dem Garbage Collector zusammen oder ich die Bagger eine Anwendung und möchte schauen, ob der Garbage Collector Probleme bereitet oder ob er ordnungsgemäß funktioniert. Ein weiterer großer Bereich ist die Performance Optimierung von Anwendungen, insbesondere Server Anwendungen, die beispielsweise viele, viele Tausend Requests verarbeiten müssen. Da spielt die Garbage Collector Optimierung oder das Tuning des Algorithmus und der Einstellung oft auch eine Rolle. Schauen wir mal ein bisschen zurück in die Geschichte.

In den 60er, 70er, 80er Jahren, also dem Zeitalter der Mainframe Computer oder später der Minicomputer, wurden die Rechner in der Assembler Sprache programmiert oder direkt in der Computersprache, die die CPU der jeweiligen Rechner auch verstanden hat. Das heißt, man hat auf einem sehr sehr niedrigen Niveau Software entwickelt und musste sich um alles kümmern, also um das Speicher Management. Also wo werden Ergebnisse abgelegt? Wie werden die Ergebnisse wieder eingeladen? Wenn ich eine Summe bilden möchte über 20 Zahlen, dann muss ich jede Zahl mit der vorherigen addieren, um dann die Endsumme zu haben.

Und kann ich einfach sagen Summe von 20 Zahlen. Das war sehr aufwendig zu entwickeln und man musste auch das Speicher Management entwickeln. Also in welchem Speicherbereich lege ich die Zwischenergebnisse ab, wo lade ich sie dann wieder ein? Und so weiter. Das heißt, man musste im Grunde sich Gedanken machen Was soll mein Programm machen? Hat die Geschäfts Logik skizzieren und wie setze ich das technisch um auf der jeweiligen Hardware? Und die Hardware war natürlich unterschiedlich von Computer zu Computer.

Diese Art der Softwareentwicklung setzte natürlich extrem gute Kenntnisse der Hardware voraus, insbesondere der CPU und der Instruction, die die CPU versteht und auch des Speicher Layouts. Also wieviel Speicher steht der CPU zur Verfügung? In welche Bereiche darf ich schreiben? Welche Bereiche sind reserviert und so weiter. Wenn ich das alles kannte, dann konnte ich sehr effiziente Programme schreiben, die passgenau auf die jeweilige Hardware angepasst wurden, die darauf dann sehr flott liefen. Der Nachteil war, dass der Entwicklungsaufwand sehr groß war.

Ich musste mir einerseits die Geschäfts Logik überlegen was soll das Programm eigentlich machen und diese dann eben entsprechend auch auf der Hardware umsetzen? Also wieviel Speicher habe ich maximal zur Verfügung für verschiedene Rechenoperationen? Wie strukturiere ich den Algorithmus möglichst Speicher, effizient und so weiter und so fort? Wenn Programmierfehler auftraten, musste man überlegen Ist das jetzt ein Fehler im Algorithmus, also in der Programm Logik oder ist das ein Fehler beim Speicher Management? Habe ich einen Fehler gemacht bei Speicher Zuweisungen?

All das konnte der Fall sein. Und die Entwicklung, Wartung, Erweiterung dieser Programme war sehr zeitaufwendig und damit auch sehr teuer. Zusätzlich waren die Programme häufig nicht portiert. Also wenn ich sie füreine CPU und einen Speicher Layout angepasst habe, war es sehr aufwendig, das Programm anschließend auf eine andere Plattform zu portieren, also beispielsweise etwa von DOS auf Mac oder auf Unix. Anfang der 80er. Das heißt, man wollte im Grunde eine Abstraktion Schicht einziehen, dass man nicht mehr direkt auf der Hardware programmieren muss geschichtlich.

In den 80er Jahren entstanden dann die ersten Hoch Sprachen. Also C C++ nehme ich jetzt hier mal als Beispiel, die die Hardware etwas mehr abstrahierten und STANDARD Funktionen anboten, also zur Textausgabe zur Dateneingabe z.B. auch Dateien lesen möchte oder Daten von der Tastatur lesen möchte. Gibt es die sogenannte C Library oder STANDARD? C Library, die das auf unterschiedlichen Plattformen gewährleistet. Das heißt, ich musste nun nicht mehr die genaue CPU und Speicher Layout kennen, sondern ich musste quasi wissen, wie programme ich gegen die C Library.

Ich musste immer noch sehr viele Hardware Details kennen. Also wie breit sind die Busse der CPU? Wie groß sind die einzelnen Daten Werte der CPU für integer? Wenn ich ein Integer abspeichern möchte usw. aber es war schon deutlich einfacher Software zu entwickeln. Der Compiler hat dann aus diesen hoch Sprachen C am Beispiel hier dann wieder Assembler Code gemacht, das dann auf der jeweiligen Hardware ausgeführt werden konnte. Okay, bei den Hoch Sprachen insbesondere C C++ muss man aber das Speicher Management auch selber entwickeln.

Also genau wie in Assembler. Muss ich mir selber Gedanken machen wann muss ich Speicher anfordern und wann muss ich Speicher wieder freigeben? Bei C macht man das mit Malak für Memory Allocation und den Fre befehlen. Das heißt, ich habe wieder Anwendungs Logic und Memory Management Logic. Dennoch war die Verfügbarkeit von Hoch Sprachen schon ein deutlicher Schritt nach vorne. Ich hatte durch den Sampler Code weiterhin relativ gute Ausnutzung der System Ressourcen des Rechners. Der Programmier Aufwand war geringer, weil ich ja jetzt die C Funktion benutzen konnte und nicht mehr für die einzelne Hardware entwickeln musste.

Aber nachteilig war, dass ich immer noch relativ viele Details über dieses Thema Architektur kennen muss. Programme eingeschränkt Portiers bar waren. Also zwischen den einzelnen C Umgebungen ging das schon mit verhältnismäßig wenig Aufwand im Verhältnis zu vorher, aber ich musste immer mich noch um die Entwicklung von Programm Logic und dem Speicher Management kümmern. Zusätzlich waren Bugs im Speicher Management sehr sehr schwierig zu finden, da einerseits die Betriebssysteme noch kein Speicher Schutz hatten, also ein wild laufender Prozess mit einem Fehler einen anderen zum Absturz bringen konnte oder das ganze Betriebssystem zum Absturz bringen konnte.

Anfang der 90er hat sich dann mit der Entwicklung neuer Programmiersprachen ein anderes Muster durchgesetzt und jetzt wird es interessant. Und zwar ist man dazu übergegangen, die Software nicht mehr zu kompilieren direkt zum Maschinencode. Das kann man immer noch machen, sondern zu interpretieren, d.h. es gab eine Software, die einen Programmcode liest, versteht und zur Ausführung Zeit eben in echte Maschinen Befehle übersetzt wie eine Art virtuelle Maschine. Diese Safa Komponente nennt man Interpreter und ein Interpreter konnte für unterschiedliche Betriebssysteme herausgegeben werden.

Linux, Unix, Mac, Windows und ein Programm, was für diesen Interpreter geschrieben wurde, kann überall auf diesen Umgebungen ausgeführt werden. Also bleiben wir mal bei einem Beispiel z.B. Python ein Python Programm kam auf Windows ausgeführt werden kann auf Linux ausgeführt werden, kann auf Mac ausgeführt werden und vielen weiteren Plattform, für die es eben eine Python Umgebung gibt. Wie gesagt, der Interpreter nimmt diesen zwischen Code und macht daraus Maschinencode. Wir haben aber den Vorteil, dass wir jetzt ein schlaues Programm, den Interpreter haben, bei parallel zu unserem eigentlichen Programm ausgeführt werden kann.

Was bietet das für Vorteile? Die Vorteile ist, dass man eine weitere Abstraktion von Hardware und Betriebssystem betreiben kann. Also ich kann einfach sagen lade die Datei und der Interpreter kümmert sich dann darum, ob das auf Windows geschehen muss, unter der Haube oder auf Linux oder auf Mac geschehen muss. Und der Interpreter verhält sich auf all diesen Umgebungen gleich. Das heißt, ich habe eine sehr gute Ports Ehrbarkeit eines Programmes zwischen den einzelnen Interpreter. Also ich kann ein Pfeifen Programm, was auf Mac entwickelt wurde natürlich ohne Probleme auch auf Linux ausführen.

Wenn ihr Pfeifen Programme schreibt, die beispielsweise auf Klout Instanzen ausgeführt werden. Macht ihr genau das. Ein Interpreter hat aber natürlich nicht nur Vorteile, sondern auch Nachteile, denn er braucht selber einigen Arbeitsspeicher und CPU Zeit, weil er ja diese Übersetzungs Schritte leisten muss. Plus man muss den Interpreter dann eben für jede CPU Betriebssystem Kombination entwickeln. Also man muss einen Interpreter für Linux haben. Ein Vermag, ein für Windows, ein für weitere Betriebssysteme, die unterstützt werden sollen.

Bei Python gibt es mehrere Environments für unterschiedliche Betriebssysteme. Bei der Java Virtual Maschinen oder JVM ist das genauso. Da gibt es auch Implementierung von Windows Linux Mac um eben ein Java Programm auf all diesen Umgebungen ausführen zu können. Der Nachteil des erhöhten Speicher Bedarfs und des CPU Bedarfs eines Interpreter. Es hat sich stark abgemeldet durch die viel größeren Speicher Größen und die deutlich gesteigerte CPU Geschwindigkeit in den Jahren. Also dieser Nachteil wurde immer geringer, weil man einfach CPU Leistung und Arbeitsspeicher im Überfluss hatte.

Damit fielen die Nachteile des Internets immer weniger ins Gewicht und man konnte den Interpreter immer mehr Aufgaben übertragen. Und die Idee der Garbage Collection, die schon bei der Programmiersprache Lisp ursprünglich konzipiert wurde, war damit dann auch wieder umsetzbar. Stand heute sind Interpreter gar nicht mehr wegzudenken und werden für die meisten neuen Programmiersprachen direkt entwickelt. Eingesetzt, zum Beispiel die Javi Mal sind die Clave virtuell Maschinen versprachen wie Java, Groovy, Scala und Cathleen. Seit einigen Jahren von Microsoft gibt es das CLS Common Language Runtime.

Für die dort Netzwelt oder für sie Schadprogramme, insbesondere aber der meist verbreitetste Interpreter, den ihr wahrscheinlich alle installiert habt oder mehrere davon ist der Web-Browser. Denn der führt JavaScript aus und JavaScript, das auch eine Hochsprache, die dann eben im Browser selber ausgeführt wird. Und der Browser interpretiert die einzelnen JavaScript Kommandos und führt dann die entsprechenden Aktionen aus. Und der Browser ist wahrscheinlich die Plattform übergreifende Software Komponente, die sowohl auf Mobiltelefonen, Tablets, allen Desktop, Betriebssystem und wahrscheinlich auch vielen Server Betriebssystemen ausgeführt werden kann.

Und überall kann eben JavaScript laufen. Diese Ausführungen Umgebungen oder Interpreter, die das eigentliche Programm ausführen, können damit dann auch die Garbage Collection übernehmen. Und jetzt sind wir beim Thema angekommen. Ich hoffe, euch hat dieser kurze geschichtliche Exkurs gezeigt, warum? Es gab Collection früher nicht gab und warum es seit Mitte der 90er eine Komponente ist, die eigentlich nicht mehr wegzudenken ist aus modernen Entwicklungsumgebung an der Garbage Collector ist also Bestandteil das Interpreter. Im nachfolgenden spreche ich einfach mal über die Java Virtual Maschienen, weil sie mir am geläufigsten ist.

Die Prinzipien gelten aber auch für alle anderen Laufzeit Umgebung, also etwa Pfeifton Environments der Garbage Collector. Der Müll Aufräumer ist eine Form des automatisierten Speicher Managements, welche sich schlichtweg durchgesetzt hat. Theist In den 70er 80er Jahren war es notwendig einerseits die Programme Logik zu implementieren und auf der anderen Seite auch darüber nachzudenken, wie ich diese Logik dann im Speicher und in der CPU behandeln möchte. Diese Speicher Management ist auf einen ganz ganz geringen Teil reduziert worden, wenn man Garbage Kollektoren einsetzt, denn die stellen selber neuen Arbeitsspeicher bereit und räumen auch nicht mehr benutzte Speicherbereich automatisch ab und stellen sie dem Interpreter wieder zur Verfügung, ohne dass man sich explizit darum kümmern muss.

Stellen also eine deutliche Vereinfachung dar und man kann sich mehr und mehr auch wirklich auf die Programme Logik konzentrieren oder auf die Geschäfts Logik, die das eigentliche Programm ausmacht. Moderne Programmiersprachen und deren Laufzeit Umgebungen bringen wie gesagt die gabelt Kollektoren mit. Es ist aber auch möglich, z.B. bei der eingangs erwähnten Programmiersprache C Nachrüsten Garbage Kollektoren mit zu integrieren, die dann Speicher Management übernimmt. Ich habe selber keine Erfahrung, ob das gut oder schlecht funktioniert, also einen nachrückt Garbage Collector zu verwenden.

Könnte mir aber vorstellen, dass da durchaus viel Forschungs Energie eingesteckt wurde, um eine hohe Qualität des Ergebnisses zu erzielen. Also dass sich das durchaus lohnt. Die Aufgabe des Garbage Collectors lässt sich vereinfacht so zusammenfassen, dass quasi parallel zur Programm Ausführung mitläuft und schaut, welcher Speicher wird der nicht mehr benötigt. Ich habe eben davon gesprochen, dass man beispielsweise 20 Zahlen auf addieren möchte, eine Einzelposten bei einer Rechnung und man möchte den Gesamtbetrag ermitteln. Und für den weiteren Verlauf des Programms ist z.B. nur noch der Gesamtbetrag wichtig und ich brauche die 20 Einzelposten nicht mehr.

Dann könnte der Garbage Collector erkennen ok. Am Ende der Funktion wird die Summe zurückgegeben und die 20 Einzel Variablen der einzelnen Rechnungen Posten werden nicht mehr benötigt. Diesen Speicher kann ich also wieder freigeben. Ja, das wäre ein typisches Garbage Collection Muster oder eine typische Garbage Collection Aufgabe. Was liegt denn generell im Speicher? Das sind Variablen, Rückgabe, Werte, Zwischenergebnisse von Berechnungen und all diese Werte können eben, wenn sie nicht mehr benötigt werden, aufgeräumt werden. Der Garbage Collector prüft also fortlaufend, welche Daten Objekte innerhalb eines Programms bleiben, aber bei der objektorientierten Programmiersprache.

Dann sind es Daten Objekte. Ansonsten sind es einfach Speicher Felder. Welche davon werden zukünftig nicht mehr gebraucht und welche davon kann ich dem Arbeitsspeicher wieder zuführen? Wenn das erledigt wird, brauch sich der Entwickler selber fast nicht mehr um das Speicher Management zu kümmern. Der Programmcode wird weniger komplex. Ich habe keinen Speicher Management Code mehr drin und automatisch wird das Programm weniger fehleranfällig. Jetzt gibt es verschiedene Ansätze, wie Garbage Collector arbeiten kann. Also wir haben ja schon gesagt, dass seine Progamm Komponente, die parallel zum eigentlichen Programm läuft und die kann jetzt entweder parallel zum Hauptprogramm Garbage Kollekten oder das Hauptprogramm anhalten, um dann in einem Rutsch nicht mehr benötigten Speicher wieder freizugeben.

Das ist die sogenannte Stop the World Pause. Da gehen mir später noch genauer drauf ein. Das muss zyklisch passieren, weil das Programm läuft ja einfach weiter benutzt weiter Variablen Habana Langlaufen und Server Anwendung könnte man davon ausgehen. Und das heißt, der Garbage Collector muss ab und an loslaufen, um nicht mehr benötigten Programm Speicher wieder freizugeben. Dabei gibt es zwei grundsätzliche Eigenschaften, die ein Garbage Collector aufweisen können und die ein bisschen gegeneinander abgewogen werden müssen wie ein Garbage Collector im Detail funktioniert und wie die Garbage Collection genau durchgeführt wird.

Das ist ein eigener Forschungsbereich innerhalb der Informatik und da gibt es mittlerweile sehr komplexe Ansätze, die sich über die Jahre stark weiterentwickelt haben. Grundsätzlich muss man aber abwägen zwischen Durchsatz oder Englisch Fru Pud optimierten Garbage Kollektoren oder GABA Kollektoren, die das ansprecht verhalten oder eine möglichst geringe Layton C zum Ziel haben. Was bedeutet das? Wenn ich auf Durchsatz optimiere, möchte ich möglichst wenig Garbage Collection Pausen haben, um einen maximalen Programms Durchsatz zu gewährleisten. Das heißt, der Garbage Collector selbst soll möglichst wenig CPU Leistung während der Programm Ausführung in Beschlag nehmen.

Das kann man z.B. bei großen Batch Jobs machen. Dafür nimmt man dann in Kauf, wenn der Garbage Collector seine Arbeit tun muss, was die Anwendung einige Sekunden lang komplett angehalten werden muss, während der Garbage Collector dann eben die nicht mehr benötigten Speicherbereich wieder freigibt. Das kann man sich vorstellen. Bei einem Batch Job wäre das nicht wirklich schlimm. Oder wenn Dateien komprimiert werden oder wenn aufwendige Berechnungen laufen, dann ist das nicht so schlimm. Hauptsache das Programm wird so schnell wie möglich fertig, auch wenn der Garbage Collector zwischendurch das Programm komplett anhalten muss, um an seine Aufgabe zu übernehmen.

Wenn ihr euch das mal vorstellen möchtet, stellt euch vor, ihr macht euer Haus das ganze Jahr nicht sauber und ruft einmal im Jahr die Putzkolonne an, die dann für drei Tage euer komplettes Haus reinigt und entrümpelt. Nur in diesen drei Tagen könnt ihr euer Haushalt nicht nutzen und müsst dann in Urlaub fahren oder auf ein anderes Zuhause ausweichen. Der Garbage Collection Prozess wird auf einmal aufgeführt und dann kann auch nichts anderes geschehen. Das wäre auf Durchsatz optimiert. Auf Programm Durchsatz optimiere ich aufs ansprecht Verhalten.

Also möchte ich, dass die Anwendung möglichst schnell antwortet. Auf Anfragen von mir, zum Beispiel in Webserver, wo Webseiten drauf liegen, die man abrufen möchte, da weiß ich ja nicht genau wann kommt der Nutzer vorbei und möchte die Webseite laden. Wenn er vorbeikommt und sie laden will, dann soll er sie möglichst sofort erhalten. Das heißt, ich möchte, wenn der gabt Collector läuft nur kurze Garbage Collection Pausen habe von wenigen Millisekunden z.B. damit die Anwendung schnell auf z.B. Nutzer, Eingaben oder andere Anfragen reagieren kann.

Beispiel könnte ein Webserver sein, ein Buchungssystem mit vielen Bestellungen oder eine interaktive Anwendungs Software, die von einem Menschen bedient wird. Der möchte natürlich auch nicht 10 Sekunden warten, wenn er irgendeinen Knopf anklickt, bis eine Rückmeldung erhält. Auch Echtzeit Anwendung wie Radar können hier reinfallen. Also ich bin einfach darauf angewiesen, dass die Anwendung sofort reagiert. Es ist aber gar nicht schlimm, wenn der Garbage Collector dann alle paar Sekunden eine ganz kurze Pause einlegt, um seine Arbeit zu tun.

Hauptsache, die Anwendung reagiert schnell. Um bei dem Beispiel der Reinigung eures Hauses zu bleiben, wäre das so, als wenn jede Woche zweimal die Putzkolonne anrückt und euer Haus generell sauber hält und jedes Mal nur eine Stunde braucht. Du kannst aber parallel einkaufen fahren oder muss nur eine Stunde dein Haus verlassen, während das durchgeführt wird. Das heißt, insgesamt wird bei der Grundreinigung in drei Tagen wahrscheinlich die Putzkolonne weniger Zeit in deinem Haus verbringen und gründlich reinigen, weil sie mit einem Wisch alles sauber macht.

Allerdings kannst du dein Haus drei Tage nicht nutzen, wohingegen. Dubai der Latenz optimierten Version dein Haus nur ganz kurz nicht nutzen kannst, dafür tritt die Situation aber öfter auf. Aber dafür kannst du das vielleicht besser ein timen und sagen Ja, eine Stunde pro Woche. Die bin ich halt nicht da und ansonsten kann ich mein Haus aber durchgehend nutzen. Genauso ist es auch bei der Garbage Collection und man muss eben zwischen diesen beiden Eigenschaften Durchsatz und ansprecht Verhalten je nach Anwendungsfall abwägen.

Meistens möchte man aber ein direktes Ansprechen Verhalten haben, gerade bei Server, Software oder interaktiver Software. Das grundsätzliche Problem also das Haus sauber zu halten, während es verwendet wird, während es bewohnt ist. Das gleiche Paradigma greift auch bei der Garbage Collection. Ich hatte eben schon gesagt, anfangs in früheren Garbage Collection Implementierungen wurde das Hauptprogramm komplett angehalten. Der Garbage Collector hat seine Arbeit verrichtet. Die alten Speicherbereich wieder freigegeben und hat die eigentliche Anwendung weiterlaufen lassen. Heute durch die gehapert Collection Forschung ist man soweit, dass sehr, sehr viele Schritte parallel zum Hauptprogramm durchgeführt werden können und das Hauptprogramm nicht blockieren.

Insbesondere auf Multi, kurz CPU mit mehreren Threats ist dies das präferierte Vorgehens Modell, weil man einfach auch mehr CPU zur Verfügung hat, die ohne Probleme parallel arbeiten können. Wie arbeitet ein Garbage Collector im Detail? Ich habe schon gesagt, ganz genau im Detail. Das wäre sehr komplex. Generell kann man aber sagen, dass der Garbage Collector die Aufgabe hat, nicht mehr benötigte Objekte oder Variablen aus dem Speicher zu identifizieren und diese dann dem Betriebssystem bzw. dem Interpreter oder der Laufzeit Umgebung zurückzugeben.

Das heißt, der Garbage Collector unterscheidet primär zwei Object Arten oder Speicher Arten. Ist das Live Objekt, d. h. ein Objekt oder ein Speicher Feld, was per Referenz, also Zeiger auf dieses Objekt noch erreicht werden kann, beispielsweise eine Datenbank Verbindung oder ein Zahlenwert und ein totes Objekt ist sist zweite Art von Object, die der Garbage Collector kennt. Das ist ein unerreichbares Objekt, also z.B. mehrere Variablen aus einer Methode, aus der ihr schon rausgesprungen seid. Diese Variablen sind ja weiterhin im Speicher.

Die Methode ist quasi beendet worden. Es gibt keine Möglichkeit mehr, auf diese Variablen zurück zu springen und die sind quasi einfach verloren. Das erkennt der Garbage Collector dann als tote Objekte an. Wie genau geht er jetzt mit den Live Objekten und den toten Objekten um? Ich möchte euch die Funktionsweise des Garbage Collectors am Beispiel des CMS oder Konkurrent Mark Sweep Garbage Collectors der Java Virtual Maschinen erklären. Der war STANDARD bis Java 8 und ist mittlerweile auch schon abgelöst worden. Aber er ist sehr einfach zu verstehen und sehr plastisch.

Dieser Garbage Collector geht von zwei Grundannahmen aus, nämlich dass die meisten jungen Objekte, also die gerade erstellt wurden, sehr schnell unerreichbar werden und sterben. Das heißt. Könnt euch vorstellen, man steigt in eine Methode ein. Es werden fünf Variablen deklariert, die zur Berechnung eines Endergebnisses benötigt werden. Das Endergebnis wird berechnet man returns. Dieses Ergebnis aus der Methode heraus und kann damit weiter rechnen. Aber die ganzen anderen Variablen in dieser Methode, die jungen Variablen, benötigt man nicht mehr.

Das ist Annahme. 1 Die meisten jungen Objekte werden schnell unerreichbar und sterben Annahme 2. Einige Objekte leben sehr lange und altern über die Zeit, wie z.B. eine Datenbank Verbindung oder ein Logger Objekt, was er quasi solange lebt wie die Anwendung selber und einfach immer nur verwendet wird an den Stellen, wo es gerade benötigt wird. Aber es kann nie abgeräumt werden vom Garbage Collector, weil die Logos Gaben ja fortlaufend geschrieben werden. Wie geht der Garbage Collector jetzt konkret vor?

Er geht in drei Phasen vor. Nochmal zur Erinnerung Er heißt Konkurrent Mark in Sweep. Die erste Phase ist die Markierung Phase, also die Marg Phase. Er läuft durch den vollständigen Object Graphen ala gerade referenzierten Objekte und markiert alle Objekte, die per Referenz noch erreichbar sind als live Objekte. Steigt ein und guckt welche Objekte kann ich alle erreichen. Da ergibt sich eine Differenz zu den erreichbaren Objekten und nicht erreichbaren Objekten. Und im nächsten Schritt löscht der alle nicht im ersten Schritt markierten Objekte, also die nicht erreicht werden konnten und die tot sind.

Die JVM führt Buch über alle Objekte und kann halt schauen welche sind erreichbar, welche sind nicht erreichbar und die nicht erreichbaren, die kann ich einfach löschen. Das ist die Sweep Phase. Die dritte Phase ist das sogenannte Compact. Zwar werden jetzt die live Objekte, die im Speicher übrig sind zusammengeschoben, um Speicher Fragmentierung zu verhindern. Das ist einfach ein Arbeitsschritt, um die Performance über die Anwendungs Lebensdauer hochzuhalten. Gehen wir das nochmal an einem konkreten Beispiel. Durch Stellt euch vor, ihr startet euer Programm.

Das Programm steigt in die erste Methode ein, berechnet verschiedene Werte. Es werden viele junge Objekte erzeugt. Diese Objekte werden in ein Speicherbereich, den sogenannten Eden Space geschrieben. Ist dieser Speicherbereich voll? Der ist gar nicht so groß, sodass das relativ schnell passieren kann. Läuft der Garbage Collector los und führt diese drei Phasen von eben durch, nämlich erlöscht alle unerreichbaren Objekte aus dem Eden Space. Im nächsten Schritt werden alle überlebenden Objekte, die noch referenziert werden könnte markiert und zwar mit einer kleinen 1 weil sie einen Zyklus, einen Garbage Collection Zyklus überlebt haben und werden durch die Kompakt Phase in den sogenannten Survivor Space gezogen.

Also man hat den Edens Space, da entstehen alle neue Objekte und dann hat man den überlebenden Speicherbereich, den zur Wahl Space, wo die Überlebenden aus dem Space hin verschoben werden. Das Programm läuft jetzt weiter. Neue Objekte entstehen im Eden Speicherbereich, also im Speicherbereich für junge Objekte, bis dieser wieder voll ist. Der Garbage Collector läuft jetzt wieder los und löscht alle toten Objekte und alle Live Objekte die überleben werden in den zur Wahl Space verschoben. Jetzt kann es aber sein, dass ein Objekt oder mehrere Objekte schon mehrere Garbage Collection Zyklen überlebt haben, sodass dort eine kleine 2 eingetragen wird, also das Live Objekt schon 2 Zyklen überlegt hat z.B. einen Logger der alle Garbage Collection Zyklen überleben wird, weil er so lange läuft, wie das Programm ausgeführt wird.

Einige Objekte werden also immer älter und werden ab Erreichen eines bestimmten Schwellwert bis in die sogenannte Old Reagen verschoben. Also irgendwann nach 30 40 Garbage Collection Zyklen sagt der Interpreter. Okay, das reicht mir. Ich weiß, dass du wahrscheinlich sehr lange leben wirst und deshalb verschiebe ich dich in einen anderen Speicherbereich, wo ich nicht jedes Mal nachgucken, ob du noch lebst, sondern vielleicht nur jedes zehnte Mal nachgucken, um einfach auch die Performance hochzuhalten. Und so entstehen nach und nach die langlebigen Objekte, sammeln sich in der OLT Region und die nicht so langlebigen Objekte werden im Eden Space oder im Cyberspace eben aufgebaut und dann auch wieder gelöscht, wenn sie nicht mehr referenziert werden können.

Das war jetzt das Beispiel des Concord Mark Sweep Garbage Collectors. Einige Zuhörer wissen vielleicht, dass ab Java 9 der sogenannte Go on, also Gabelt First Collector zum STANDARD geworden ist. Der verfährt nach einem ähnlichen Muster, aber benutzt noch viele, viele weitere Tricks, um den Speicherbedarf gering zu halten. Zum Beispiel nutzt er die String Dita Applikation per Default. Da geht es einfach darum, dass unterschiedliche Untersuchungen haben gezeigt, dass ungefähr 20 bis 30 prozent des Speicherbedarf seiner Anwendung wird für Zeichenketten aufgewendet, also Texte und dass die Texte häufig gleich sind.

z.B. Könnte man sich vorstellen ein Login Bereich für unterschiedliche Benutzer, dass viele viele Benutzer, die eingeloggt sind, oft die gleichen Texte sehen und das pro Benutzer dann die Texte vorgehalten werden um Westring. Die Applikation geht der Garbage Collector eben hin und findet identische Strings, löscht identische Strings außer einer Kopie und biegt alle Zeiger auf diese eine Kopie um, sodass ich den String wirklich im Speicher nur einmal vorhalte und alle anderen Stellen, die diesen String benötigen, diese Zeichenkette benötigen, zeigen eben auf diese Stelle.

Und ja, man spart sich ein vielfaches Vorhalten des Strings. Das ist eine Technologie, die die Givaan Garbage Collector einsetzen kann. Jeder Garbage Collector, jede Implementierung hat unterschiedliche Eigenschaften in Bezug auf Overhead Pausen, Frequenz des Aufrufen aus den eigenen Speicherbedarf und auch die Skalierbarkeit. Also hier insbesondere für sehr große Anwendungen, die Hunderte von Gigabyte SRAM benötigen. Da muss man spezielle Algorithmen verwenden, dass diese Garbage Collection Pausen nicht sehr, sehr, sehr lange werden. Und das stellt auch eine große Herausforderung dar.

Viele, viele Aufgaben eines Garbage Collectors können heute auf Multi CPU System parallel zur Programm Ausführung durchgeführt werden. Das ist natürlich ideal. Bama möchte diese Pause des Programms verhindern. Die gefürchtete Stop the World Pause oder Halte die Welt an Pause ist aber immer noch möglich bei den STANDARD Implementierungen heute. Dann muss die Anwendung wirklich komplett angehalten werden, damit sich der Object Baum nicht während des Garbage Collection Zyklus ändert. Das Programm läuft natürlich weiter und der Garbage Collector muss die alten Objekte löschen.

Aber das kann er natürlich. Ab einem gewissen Punkt muss er das Programm anhalten, um sicher zu sein, nicht Objekt zu löschen, die vielleicht gerade erst entstehen. Diese Pause, dieses Stop the World Pause gilt es eben möglichst kurz zu halten in allen Implementierungen. Und teilweise sind diese Pausen ein echtes Problem für Server Anwendungen, die 1 000 Finanztransaktionen pro Sekunde bewältigen müssen und die sehr viel Arbeitsspeicher benötigen und die sehr schnell fortlaufend antworten müssen. Da kann einer Stop the World Pause wirklich den Service beeinträchtigen oder auch zu Fehlern führen.

Es gibt Spezial Anbieter. Im Jawa Bereich oder im J. Wie im Bereich. Die haben viel geforscht und bieten tatsächlich Non Blocking Garbage Collection Implementierungen, die dieses Stop the World Pause nicht mehr haben und für sehr, sehr große Anwendungen sind diese kostenpflichtigen Javi Ems dann wirklich von Vorteil, da man eben Speicher im Terabyte Bereich adressieren kann, ohne fürchten zu müssen, dass die Stop the World Pause die Anwendung für einen kurzen Zeitpunkt nicht nutzbar macht. Auch ein Garbage Collector enthält natürlich euer Web-Browser bei der Ausführung von JavaScript.

Hier kann man natürlich sich voll auf die Business Logik konzentrieren und muss keinerlei Speicher Management machen. Wenn ihr schon mal JavaScript entwickelt habt, dann wisst ihr das natürlich und habt das wahrscheinlich nie vermisst. Aber früher musste man tatsächlich um die Anwendungs Logik drumherum auch die Speicher Logik implementieren. Die unterschiedlichen Garbage Collection Implementierungen können natürlich auch unterschiedlich getunt werden, je nach Vorliebe. Je nachdem, ob man Richtung Durchsatz optimieren will oder ob man Richtung Latenz optimieren will, empfiehlt es sich, unterschiedliche Garbage Collection Algorithmen zu verwenden.

Das kann man bei einigen Interpreter wirklich einstellen per Command Line Flag, welchen Garbage Collector man nutzen möchte mit den jeweiligen Vor und Nachteilen. Ein Tuning ist diese String, die Applikation. Die hatte ich schon angesprochen. Da packe ich einen Link in die Shownotes rein. Eine andere Optimierung kann die so genannte Kompression von Zeigern sein. Auf 64 Bit System nehmen die sehr viel Raum in Anspruch. Einer sind 64 Bit groß und oftmals, wenn man gar nicht so viel Speicher benötigt, kann man da per Offset noch die Speicher Zeiger z.B. auf 32 Bit kürzen und so die Programm Geschwindigkeit weiter steigern.

Mich würde interessieren, was ihr zum Thema Garbage Collection denkt, welche Erfahrungen ihr schon gemacht habt, welche Infos ihr noch habt, die ihr unbedingt teilen möchtet. Wenn ihr Zuhörer fragen habt oder Feedback zu aktuellen Podcast Episode sendet uns gerne eine E-Mail an Podcast Skillbyte. Wir freuen uns immer über Bewertungen und Abonnements unseres Podcasts und ganz besonders an die Weiterempfehlung an eure Freunde und Kollegen, die sich ebenfalls für Technologie interessieren. Schaut auch gerne auf Skillbyte Slash Blog vorbei für weitere spannende Technologie, Tim.

Ich wünsche euch einen schönen Tag. Macht's gut.