Abgrenzung zur Referenzzählung


Alle Seminare - Inhaltsübersicht - Vorher: Einleitung - Nächstes: Referenzfolgende Garbage Collectoren

Übersicht: Abgrenzung zur Referenzzählung


Was bedeutet Referenzzählung?

Bei der Referenzzählung (reference counting) enthält jedes Objekt einen Zähler, wieviele Referenzen auf es existieren. Wird eine Referenz auf das Objekt auf eine Speicherstelle zugewiesen, wird der Zähler um eins erhöht. Wird einer Speicherstelle, die eine Referenz auf das Objekt enthält, ein neuer Wert zugewiesen oder verläßt die Speicherstelle ihren Gültigkeitsbereich, wird der Zähler um eins erniedrigt. Erreicht der Zähler den Wert Null, wird das Objekt freigegeben.

Die Referenzählung wurde etwa zur gleichen Zeit und als eine Alternative zum Garbage Collecting entwickelt. Inzwischen wird mit Garbage Collecting alle Formen der automatischen Speicherverwaltung bezeichnet, also sowohl die Referenzzählung als auch Referenzen folgende (tracing) Garbage Collectoren.

Vor- und Nachteile

Referenzählung ist recht simpel und daher einfach zu implementieren. Die Last der Speicherverwaltung wird gleichmäßig verteilt zwischen allen Threads und Anweisungen, so daß keine längeren Pausen entstehen wie bei einem Durchlauf eines Garbage Collectors. Pausen sind aber durchaus möglich, wenn durch das Freigeben eines Objektes Referenzen ungültig werden und so weitere Objekte ebenfalls freigegeben werden müssen, so eine Kaskade zum Freigeben von Strukturen kostet entsprechend Zeit.

Leider gibt es auch gewichtige Nachteile. Da ausschließlich Referenzen gezählt werden, hat man bei selbstreferenzierenden Strukturen, beispielsweise Ringlisten, das Problem, daß der Zähler niemals auf Null geht. Bei speziellen Klassen läßt sich dies unter Umständen umgehen, bei einem komplett allgemeinem Garbage Collection System, ist dies aber nicht einfach zu ändern. Dadurch bleiben Strukturen weiterhin im Speicher, die gar nicht mehr von außen referenziert werden und daher nicht mehr genutzt werden können.

Das zweite Problem ist das Zählen an sich. Dies muß jedesmal geschehen, wenn eine Referenz zugewiesen wird, und dies meist sogar zweimal, da der Zähler des vorher referenzierten Objektes ebenfalls vermindert werden muß. Außerdem muß dies auch immer beim Verlassen des Gültigkeitsbereiches von entsprechenden Referenzspeicherstellen, beispielsweise lokale Variablen, geschehen, auch und insbesondere falls ein Fehler auftritt. Das bedeutet auch, daß Variablen immer initialisiert werden müssen, so daß die Zählung nicht auf ungültige Speicherbereiche erfolgt. Dies kostet Prozessorzeit, und da Zuweisungen durchaus häufig sind, ist es eine nicht zu vernachlässigende Effizienzminderung. Bei den meisten Systemen wird hierfür auch eine Unterstützung des Compilers benötigt, der entsprechenden Code vor und nach Zuweisungen, sowie beim Betreten und Verlassen eines Gültigkeitsbereichs erzeugt.

Da bei heutigen Sprachen mit Fehlerbehandlung dies auch im Falle eines Fehlers geschehen muß, erfordert dies zusätzlichen Aufwand. Oft wird einfach das gleiche von der Sprache zur Verfügung gestellte Konzept zur Fehlerbearbeitung dafür benutzt. Dieser Zusatzaufwand und die Sicherheit, daß auch im Falle eines Fehlers keine Speicherlecks durch mangelndes Verringern von Referenzzählern geschehen, können auch Optimierungen verhindern, die ohne die Referenzzählung erfolgen könnten.

Heutige Nutzung der Referenzzählung

Heutzutage wird die Referenzählung nur noch selten als allgemeiner Garbage Colector verwendet, angeblich ist dies noch in Microsofts Visual Basic der Fall.

In Spezialfällen trifft man Referenzzählung aber durchaus häufiger an. Schnittstellen (Interfaces), abgeleitet von abstrakten C++ Klassen, stellen grade unter Microsoft Windows einen Teil der neueren API da, beispielsweise COM oder DirectX, aber auch das plattformunabhängige CORBA.

In Delphi trifft man sie auch gleich mehrmals. Zum einen unterliegen Schnittstellen dort automatisch einer Referenzzählung, anders als in ihrer Herkunftssprache C++, zum anderen wird für Strings und Dynamischen Arrays automatisch ein Referenzzähler verwaltet. Während bei Strings dies eher für schnelleres Zuweisen eines Strings auf einen anderen gedacht ist und eine automatische Copy-On-Write-Semantik von Nöten ist beim Ändern eines String um die anderen ihn referenzierenden nicht ebenfalls zu ändern, kommt bei den Dynamischen Arrays wieder das normale Verhalten, mehrere Referenzen auf einen gemeinsamen Speicherbereich, durch. In beiden Fällen trifft aber zu, daß keine Referenzen auf sich selber oder andere Objekte möglich ist, dies erlaubt das sichere Benutzen und trifft man bei den meisten Typen an, die Referenzzählung verwenden.

In einer etwas anderen Form der Speicherverwaltung findet man die Referenzzählung in Unix-Dateisystemen, beispielsweise ext2. Dateieinträge enthalten dort eine inode-Nummer, die inode enthält dann die eigentlichen Informationen über die Datei und wie man auf den Inhalt zugreift. Sie enthält auch einen Zähler, wie oft sie referenziert wird, so daß die Datei freigegeben wird, wenn die letzte Referenz gelöscht wird. Referenzen auf Dateien kann man in Form eines Hard-Links mit dem Befehl "ln" erstellen. Bei Verzeichnissen ist dies nicht möglich, da dort automatisch vom Dateisystem Referenzen verwendet werden, eine für den eigentlichen Eintrag vom Elternverzeichnis, eine für den Eintrag "." im Verzeichnis auf sich selber und dann jeweils eine für die Einträge ".." in jedem Unterverzeichnis.


Alle Seminare - Inhaltsübersicht - Vorher: Einleitung - Nächstes: Referenzfolgende Garbage Collectoren Seitenanfang