Über die Zeit gab es etliche Verbesserungen bei Garbage Collectoren, einer der wichtigsten ist die Aufteilung in Objekte verschiedener Generationen, die Garbage Collection selbst konkurrenzfähig machte zur konventionellen Speicherverwaltung. Je nach verwendeter Wirtssprache und Umgebung lassen sich die Algorithmen noch optimieren, über Parameter und Auswahl von Algorithmen auch für bestimmte Programme die schließlich mit dem Garbage Collector laufen sollen. Dies gilt zumindest für die allgemeineren Versionen der stärker genutzen Sprachen. Suns und IBMs JVMs sind ein Beispiel, aber auch die vom Glasgow Haskell Compiler erzeugte Laufzeitumgebung erlaubt die Auswahl und Parametrisierung des Garbage Collectors.
Eine neue Technik, die wahrscheinlich noch in keinem Garbage Collector bislang umgesetzt wurde, ist die Escape Analysis. Dabei wird versucht, sehr kurzlebige Objekte zu identifizieren, die nur in einem bestimmten Gültigkeitsbereich auftauchen, meist Funktionen. Wird festgestellt, daß das Objekt nur dort verwendet wird und keine Referenz nach außen gelangt, durch Zuweisung an ein anderes Objekt oder als Funktionsergebnis, kann das Anlegen und Freigeben optimiert werden, indem es direkt auf dem Stack erzeugt wird. Dies ähnelt dann dem Verhalten von zu Maschinencode compilierten Hochsprachen.
Durch dieses Verfahren können Objekte ohne zusätzliche Kosten für die Speicherverwaltung genutzt werden. Dazu wird allerdings eine große Compilerunterstützung benötigt. Dies kann in Hinsicht austauschbarer Compiler, Laufzeitumgebungen und Garbage Collectoren manchmal nicht akzeptabel sein. Eine andere Möglichkeit wäre ein sowieso zunehmend verbreitetes Feature von Umgebungen, die in einer Virtuellen Maschine laufen, zu nutzen. Bei der Just-In-Time (JIT) Compilierung wird direkt vor der Ausführung eines Programmstücks (z.B. einer Funktion), diese in von der Hardware direkt ausführbaren Maschinencodes übersetzt. JIT Compilierung wird bereits seit längerer Zeit für Sprachen wie Smalltalk und Java eingesetzt. Während dieser Neucompilierung kann auch gleichzeitig für die Speicherverwaltung die Escape Analysis durchgeführt werden.
Es folgen ein paar Tips für die Entwicklung in Sprachen und Umgebungen, die garbage collectet sind. Sie gelten besonders für Java, sollten aber im Allgemeinen auch für andere Umgebungen einsetzbar sein.
Der beste Tip bei heutigen Garbage Collectoren, ist keine Tricks mehr anzuwenden. Bei den einfacheren Garbage Collectoren gab es Tips wie häufig genutzte Objekte auf Vorrat anzulegen, und diese dann selber zu verwalten beim Anfordern und Freigeben, um das damals kostspieligere Anlegen und Freigeben zu vermeiden. Da dies bei den heutigen nicht mehr zu trifft, sollte diese Technik nur noch in äußerst seltenen Spezialfällen verwendet werden. Durch das manuelle Verwalten kann es wieder zu den vielen verschiedenen Fehlern kommen, die man durch die Garbage Collection eigentlich vermeiden wollte, wie Speicherlecks, wenn vergessen wird Objekte freizugeben, oder Referenzen auf Objekte, die eigentlich schon freigegeben wurden. Oder es werden Ressourcen belegt, während sie nicht benutzt werden, und unter Umständen auch weitere Objekte referenziert in eigentlich gelöschten Objekten, die dann ebenfalls nicht vom Garbage Collector gelöscht werden können.
Das explizite Löschen von Referenzen, so daß die vormals referenzierten Objekte unter Umständen freigegeben werden könnte, sollte auch nicht zu extrem eingesetzt werden, der Garbage Collector soll sich um das Freigeben des Speichers kümmern, nicht der Programmierer. Lediglich in Fällen, wo Objekte außerhalb ihres logischen Gültigkeitsbereichs noch referenziert aber nicht mehr benötigt werden, sollte dies möglichst durch Löschen der Referenz behoben werden. Beispiele sind statische Variablen, die zum Puffern von Objekten für einige Zeit von mehreren Funktionen verwendet werden bis ein Algorithmus abgearbeitet wurde, danach sollte der Puffer entsprechend gelöscht werden. Ein anderes Beispiel wäre ein einfacher Stack als eine Liste, nach dem Entfernen eines Elementes, sollte die Referenz in der Liste ebenfalls gelöscht werden, da der Eintrag innerhalb des Stacks nicht mehr gültig ist und das Freigeben des Objektes dadurch nicht behindert werden sollte.
Das manuelle Ausführen des Garbage Collectors sollte meist ebenfalls
vermieden werden. Grade in Java ist es via System.GC();
nur möglich einen kompletten Durchlauf auszuführen, während bei den
heutigen Algorithmen normalerweise nur der Abschnitt mit den jüngsten
Objekten aufgeräumt werden braucht. Normalweise sollte dem Garbage
Collector die Entscheidung überlassen werden, ob und wann eine
Säuberung stattfinden soll. Das manuelle Ausführen ist nur zu
Debug-Zwecken sinnvoll, um zu prüfen, wie viele Objekte zwischen zwei
manuellen Säuberungen (z.B. vor und nach einem Algorithmus) verwendet
werden. Dadurch kann man auch bestimmen, ob es Speicherlecks gibt.
Speicherlecks an sich gibt es natürlich nicht, wenn ein Garbage
Collector verwendet wird, aber wenn eigentlich nicht mehr verwendetet
Objekte unnützerweise weiterhin referenziert werden, hat dies einen
ähnlichen Effekt.
Die Verwendung von Methoden, die beim Freigeben eines Objektes aufgerufen werden (finalizers), sollte vermieden werden. Diese Methoden werden erst aufgerufen, wenn der Garbage Collector sie löscht. Da nicht sichergestellt ist wann und ob er dies überhaupt tut, kann die Verwendung zu Problemen führen. Grade Objekte, die etwas länger leben und in Speicherbereiche der älteren Objekte verschoben wurden, werden meist erst sehr spät gelöscht, da diese Bereiche seltener vom Garbage Collector gesäubert werden. Das Ausführen der Methoden ist außerdem sehr aufwendig für den Garbage Collector, da hierbei auch vorerst alle durch das freizugebende Objekt referenzierten Objekte im Speicher gehalten werden müssen um während der Ausführung zur Verfügung zu stehen. Dies benötigt einen extra Test für alle zu löschenden Objekte. Grade für kopierende Garbage Collectoren ist dies ein zusätzlicher Aufwand, da sie zu löschende Objekte eigentlich nicht mehr bearbeiten. Es ist auch nicht klar, was passiert, wenn sich zwei Objekte gegenseitig referenzieren, die finalizer verwenden, also in welche Reihenfolge sie aufgerufen werden, was wieder zu Problem führen kann, wenn ein Objekt dabei auf das andere zugreifen will.
Etwas, was man tun kann, um den Garbage Collector zu unterstützen, ist, unveränderliche (immutable) Objekte zu verwenden. Objekte, die Referenzen auf andere Objekte enthalten können, können für Garbage Collectoren, die das Alter der Objekte betrachten optimiert werden, indem sie unveränderlich gemacht werden. Sprich, alle Datenfelder werden nur beim Anlegen des Objektes zugewiesen und sind danach nur über Zugriffsmethoden zu lesen und nicht mehr zu schreiben. Der Grund ist recht einfach: Kann man die Referenzen auf andere Objekte zeigen lassen, sind die neureferenzierten Objekte meist neu angelegte und damit junge Objekte, andersrum sind die referenzierenden Objekte oft alt, da man durch das Verändern normalweise grade das Neuanlegen sparen möchte. Also wird oft einem alten Objekt eine Referenz zu einem neuem zugewiesen. Bei Garbage Collectoren, die die Objekte nach ihrem Alter aufteilen, müssen aber genau solche Zuweisungen jeweils überprüft und das junge Objekt in einer Liste vermerkt werden. Unveränderliche Objekte haben auch eine Reihe weiterer brauchbaren Eigenschaften, so daß dies keine reine Optimierung für den Garbage Collector sein muß.
Alle Seminare - Inhaltsübersicht - Vorher: Praktische Garbage Collectoren - Nächstes: Literatur | Seitenanfang |