Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Die Basis der Objektorientierung
3 Die Prinzipien des objektorientierten Entwurfs
4 Die Struktur objektorientierter Software
5 Vererbung und Polymorphie
6 Persistenz
7 Abläufe in einem objektorientierten System
8 Module und Architektur
9 Aspekte und Objektorientierung
10 Objektorientierung am Beispiel: Eine Web-Applikation mit PHP 5 und Ajax
A Verwendete Programmiersprachen
B Literaturverzeichnis
Stichwort
Ihre Meinung?

Spacer
 <<   zurück
Objektorientierte Programmierung von Bernhard Lahres, Gregor Rayman
Das umfassende Handbuch
Buch: Objektorientierte Programmierung

Objektorientierte Programmierung
2., aktualisierte und erweiterte Auflage, geb.
656 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1401-8
Pfeil 7 Abläufe in einem objektorientierten System
  Pfeil 7.1 Erzeugung von Objekten mit Konstruktoren und Prototypen
    Pfeil 7.1.1 Konstruktoren: Klassen als Vorlagen für ihre Exemplare
    Pfeil 7.1.2 Prototypen als Vorlagen für Objekte
    Pfeil 7.1.3 Entwurfsmuster »Prototyp«
  Pfeil 7.2 Fabriken als Abstraktionsebene für die Objekterzeugung
    Pfeil 7.2.1 Statische Fabriken
    Pfeil 7.2.2 Abstrakte Fabriken
    Pfeil 7.2.3 Konfigurierbare Fabriken
    Pfeil 7.2.4 Registraturen für Objekte
    Pfeil 7.2.5 Fabrikmethoden
    Pfeil 7.2.6 Erzeugung von Objekten als Singletons
    Pfeil 7.2.7 Dependency Injection
  Pfeil 7.3 Objekte löschen
    Pfeil 7.3.1 Speicherbereiche für Objekte
    Pfeil 7.3.2 Was ist eine Garbage Collection?
    Pfeil 7.3.3 Umsetzung einer Garbage Collection
  Pfeil 7.4 Objekte in Aktion und in Interaktion
    Pfeil 7.4.1 UML: Diagramme zur Beschreibung von Abläufen
    Pfeil 7.4.2 Nachrichten an Objekte
    Pfeil 7.4.3 Iteratoren und Generatoren
    Pfeil 7.4.4 Funktionsobjekte und ihr Einsatz als Eventhandler
    Pfeil 7.4.5 Kopien von Objekten
    Pfeil 7.4.6 Sortierung von Objekten
  Pfeil 7.5 Kontrakte: Objekte als Vertragspartner
    Pfeil 7.5.1 Überprüfung von Kontrakten
    Pfeil 7.5.2 Übernahme von Verantwortung: Unterklassen in der Pflicht
    Pfeil 7.5.3 Prüfungen von Kontrakten bei Entwicklung und Betrieb
  Pfeil 7.6 Exceptions: Wenn der Kontrakt nicht eingehalten werden kann
    Pfeil 7.6.1 Exceptions in der Übersicht
    Pfeil 7.6.2 Exceptions und der Kontrollfluss eines Programms
    Pfeil 7.6.3 Exceptions im Einsatz bei Kontraktverletzungen
    Pfeil 7.6.4 Exceptions als Teil eines Kontraktes
    Pfeil 7.6.5 Der Umgang mit Checked Exceptions
    Pfeil 7.6.6 Exceptions in der Zusammenfassung


Rheinwerk Computing - Zum Seitenanfang

7.3 Objekte löschen  Zur nächsten ÜberschriftZur vorigen Überschrift

Objekte haben in der Regel eine begrenzte Lebensdauer. Das heißt, dass sie irgendwann ihren Zweck erfüllt haben und innerhalb des laufenden Systems nicht mehr benötigt werden. Da ein Objekt durchaus Ressourcen belegt und diese immer begrenzt sind, sollten Objekte in solchen Fällen gelöscht werden.


Rheinwerk Computing - Zum Seitenanfang

7.3.1 Speicherbereiche für Objekte  Zur nächsten ÜberschriftZur vorigen Überschrift

Grundsätzlich gibt es drei Speicherbereiche, in denen Objekte und deren Datenelemente abgelegt werden können. Je nach Speicherbereich unterscheidet sich das Verfahren, nach dem die Lebensdauer der Objekte bestimmt wird.

Statischer Speicher

Im statischen Speicher werden Daten verwaltet, die für die gesamte Laufzeit der Anwendung oder zumindest bestimmter Module der Anwendung gültig sind. So werden zum Beispiel in C++ im statischen Speicher die globalen Variablen oder die klassenbasierten Datenelemente gehalten. Diese Daten werden beim Start der Anwendung, beziehungsweise beim Laden eines Moduls, erzeugt und erst beim Beenden der Anwendung gelöscht. Sie können nicht dynamisch während der Anwendung erzeugt oder gelöscht werden.

Dynamischer Speicher

Das kann man nur mit Objekten machen, deren Daten im dynamischen Speicher gehalten werden. Beim dynamischen Speicher unterscheidet man zwischen dem Stack und dem Heap.

Stack

Der Stack wird verwendet, um lokale Variablen der Routinen zu speichern. Die Lebensdauer einer lokalen Variablen ist auf die Laufzeitdauer der Routine, in der sie deklariert wurde, beschränkt. Sie werden erzeugt, nachdem die Routine startet, und gelöscht, bevor sie endet. Sie werden immer in umgekehrter Reihenfolge ihrer Erzeugung gelöscht. Die durch solche Variable referenzierten Datenstrukturen werden sozusagen »aufeinander gestapelt«, daher auch der englische Name Stack. In einer Anwendung kann es mehrere Stacks geben, jeder nebenläufige Thread hat einen eigenen. Die einfache Struktur und die klar definierte Lebensdauer der lokalen Variablen sind Vorteile des Stacks. Der Nachteil ist, dass die Datenstrukturen, die auf dem Stack gespeichert werden, nicht die Laufzeit einer Routine überstehen.

Heap

Diese Möglichkeit bietet der Heap. Auf dem Heap kann eine Datenstruktur zu beliebiger Zeit dynamisch erzeugt werden. Kann sie aber auch zu jeder beliebigen Zeit gelöscht werden? Das hängt von der verwendeten Programmiersprache ab.

Im Gegensatz zur Objekterzeugung, die der Programmierer explizit bestimmen muss, gelten für das Entfernen der auf dem Heap angelegten Objekte ähnliche Regeln wie für das Entfernen der Objekte von dem Stack.

Regeln für das Entfernen von Objekten von Stack und Heap

Objekte sollen erst entfernt werden, wenn sie nicht mehr gebraucht werden, nicht früher. Nun, es wäre extrem schwierig festzustellen, ob ein Objekt tatsächlich noch gebraucht wird, und daher beschränkt man sich auf eine etwas entschärfte Forderung: Die Objekte sollten entfernt werden, wenn sie nicht mehr erreichbar sind. Das heißt, sie sollten dann gelöscht werden, wenn sie von keinem aktiven Teil der Anwendung mehr referenziert werden.

Der auf den ersten Blick einfachste Weg, mit nicht mehr benötigten Objekten umzugehen, ist es, das Aufräumen dem Programmierer zu überlassen. [Das ist nicht ganz korrekt. Noch einfacher ist es, einfach überhaupt nicht aufzuräumen und die nicht mehr gebrauchten Objekte im Speicher zu belassen. Dies ist jedoch nur für sehr kleine Systeme, die nur kurze Einsatzzeiten haben, wirklich praktikabel. ] In Sprachen, die keine automatische Speicherverwaltung integriert haben, geschieht dies in der Regel über eine spezielle Operation. In C++ wird das Löschen eines Objekts zum Beispiel über den delete-Operator vorgenommen. Dieses Vorgehen ist jedoch sehr fehleranfällig, es bestehen vielfältige Möglichkeiten, Probleme in das System einzubauen. Es ist nämlich für einen Programmierer oft schwer zu entscheiden, wann ein bestimmtes Objekt wieder freigegeben werden kann. Wird ein Objekt gelöscht, obwohl es von anderen Objekten noch referenziert wird, kommt es zu Fehlern im späteren Programmablauf.


Rheinwerk Computing - Zum Seitenanfang

7.3.2 Was ist eine Garbage Collection?  Zur nächsten ÜberschriftZur vorigen Überschrift

Um das zu vermeiden, haben Systeme wie Java in ihrem Laufzeitsystem eine automatische Speicherbereinigung integriert. Durch diesen Automatismus wird eine der häufigsten Fehlerquellen, nämlich Verweise auf bereits gelöschte Objekte, eliminiert.

Dieser Mechanismus nennt sich Garbage Collection.


Icon Hinweis Garbage Collection (automatische Speicherbereinigung)

Wenn ein Objekt von keinem anderen Objekt oder sonstigen Systembestandteil mehr referenziert wird, kann es nicht mehr gefunden und somit auch nicht mehr genutzt werden. Trotzdem ist das Objekt noch da und belegt Systemressourcen. Und darin liegt die Analogie zu den Abfällen (engl. Garbage): Diese liegen nur herum, brauchen Platz und ... fangen irgendwann an zu stinken. Bei Objekten ist Letzteres zwar nur in seltenen Fällen gegeben, aber genauso wie beim Abfall muss jemand die nutzlosen Objekte identifizieren und entsorgen. Dieser Mechanismus des Auffindens und Entsorgens wird Garbage Collection genannt.


Die Regel, Objekte, die nicht mehr erreichbar sind, zu entfernen, lässt sich zwar leicht formulieren, sie zu implementieren ist aber nicht so einfach. Aus diesem Grund gibt es verschiedene Verfahren, wie sich der Mechanismus der Garbage Collection umsetzen lässt. In den folgenden Abschnitten werden wir diese Verfahren beschreiben.

Ein Garbage Collector ist zum Beispiel in den Programmiersprachen Java, C# und Smalltalk, genauer gesagt in deren Laufzeitsystem, bereits eingebaut. Da auch die Objekterzeugung durch dieses Laufzeitsystem erfolgt, hat das System die komplette Kontrolle über die verwalteten Objekte.

Somit ist es in diesen Sprachen ausgeschlossen, dass Fehler dadurch auftreten, dass noch Referenzen auf bereits freigegebene Objekte existieren. Sofern eine solche Referenz aber noch existiert, wird der Mechanismus der automatischen Speicherbereinigung dafür sorgen, dass das referenzierte Objekt nicht gelöscht wird.

Einige Programmiersprachen bieten auch die Integration einfacher Möglichkeiten des Cachings von Objekten, indem diese Objekte zu einem gewissen Grad vor der Garbage Collection geschützt werden. Dieser Mechanismus wird in Java als Nutzung von weichen Referenzen (Soft References) bezeichnet.

Soft Reference und Caches


Weiche Referenzen (Soft References)

In Java kann man einen von Speicherknappheit abhängigen Cache durch die Verwendung von weichen Referenzen (Klasse SoftReference) implementieren. Ist ein Objekt nicht mehr direkt erreichbar, sondern nur noch über SoftReferences, so wird es so lange vom Garbage Collector in Ruhe gelassen, wie dem Programm genügend Speicher zur Verfügung steht. Wird der Speicher knapp, werden auch die Objekte, die nur über SoftReferences erreichbar sind, gelöscht.



Rheinwerk Computing - Zum Seitenanfang

7.3.3 Umsetzung einer Garbage Collection  topZur vorigen Überschrift

Wenden wir uns nun den verschiedenen Algorithmen zu, über die Verfahren zur automatischen Speicherbereinigung umgesetzt werden können.

Arten von Garbage Collection

Wir können drei grundsätzliche Arten von Garbage Collection unterscheiden:

1. Zählen von Referenzen auf Objekte (Reference Counting)
       
2. Markieren von referenzierten Objekten Markieren von referenzierten Objektenmit anschließender Bereinigung (Mark and SweepMark and Sweep)
       
3. Kopieren aller referenzierten Objekte in einen neuen Speicherbereich und Anpassen aller Verweise darauf (Copying Collection)
       

Und dann gibt es noch eine ganze Reihe von Verfahren, welche die genannten kombinieren, um deren Nachteile weitgehend auszugleichen. Wir stellen hier die genannten Verfahren kurz vor. [Eine ausführliche Diskussion von optimierten Verfahren zur Garbage Collection findet sich zum Beispiel in der Dokumentation von Sun zur Garbage Collection der virtuellen Maschine ab Version 1.4. Die virtuelle Maschine von Java erlaubt aktuell die Auswahl aus vier unterschiedlichen Verfahren zur Garbage Collection. Der entsprechende Artikel findet sich unter http://java.sun.com/docs/hotspot/gc1.4.2/. ]

Von den oben genannten Ansätzen ist der des Zählens von Referenzen am einfachsten. Wir beginnen deshalb mit dessen Vorstellung, um zunächst einmal seine Grenzen zu verstehen.

Zählen von Referenzen (Reference Counting)

Ein einfacher und intuitiver Ansatz, um eine automatische Verwaltung des belegten Speichers zu erreichen, ist das sogenannte Zählen von Referenzen (Reference Counting).

Objekt führt Buch über Referenzen.

Dabei werden – wie durch den Namen impliziert – alle Referenzen auf ein Objekt gezählt, das Objekt selbst führt darüber Buch und merkt sich die Zahl der Referenzen. Gibt es keine Referenzen auf das Objekt mehr, kann es gelöscht werden. Wird eine neue Referenz hinzugefügt, wird das Objekt darüber benachrichtigt und der Zähler heraufgesetzt, wird eine Referenz entfernt, so wird der Zähler um eins heruntergesetzt.

Dazu sind ein paar Voraussetzungen notwendig:

  • Bei Ausführung jeder Operation, die eine Referenz hinzufügt, muss der Zähler des betroffenen Objekts hochgezählt werden. So wird zum Beispiel bei Nutzung des Zuweisungsoperators hochgezählt, da es danach einen weiteren Verweis auf das Objekt gibt.
  • Bei Ausführung jeder Operation, die eine Referenz entfernt, muss der Zähler des betroffenen Objekts heruntergezählt werden. Auch hier ist der Zuweisungsoperator betroffen; wenn darüber einer Variablen ein neuer Wert zugewiesen wird, muss der Zähler für den bisherigen Wert heruntergezählt werden, da jetzt eine Referenz weniger existiert.
  • Erreicht der Zähler den Stand 0, muss das betroffene Objekt gelöscht werden.

In Abbildung 7.22 sind zwei Klassen dargestellt, die für die Umsetzung einer Speicherbereinigung in C++ verwendet werden können.

Abbildung 7.22    Zählen von Referenzen in C++

Dabei werden die eigentlichen Objekte (Exemplare der Klasse RefCounted) in sogenannten Smart Pointern, in diesem Fall Exemplare der Klasse RefCountBasePtr, gekapselt. Bei Konstruktion und Zuweisungen wird dann nur noch mit RefCountBasePtr gearbeitet. Um auf das eigentliche Objekt durchzugreifen, das über m_pTarget referenziert wird, ist der Operator -> überladen. Dieser wird beim Zugriff das enthaltene Exemplar von RefCounted liefern. In Listing 7.26 ist die Umsetzung dargestellt. Dabei wird ersichtlich, dass der Referenzzähler auf dem eigentlichen Objekt jeweils angepasst wird.

class RefCountPtrBase 
{ 
public: 
    RefCountPtrBase(RefCounted const* pTarget):     
                     m_pTarget(pTarget) { 
            m_pTarget->AddRef(); 
    } 
 
    RefCountPtrBase(RefCountPtrBase const& another):  
                     m_pTarget(another.m_pTarget) { 
            m_pTarget->AddRef(); 
    } 
    ~RefCountPtrBase() {   
            m_pTarget->Release(); 
    } 
    RefCountPtrBase& operator= (RefCountPtrBase const&   
                                another) { 
        another.m_pTarget->AddRef(); 
        m_pTarget->Release();        
        m_pTarget = another.m_pTarget; 
        return *this; 
    } 
    // ... 
} 
 
class RefCounted 
{ 
   signed long mutable m_nRefCount; 
   // ... 
   void AddRef() {        
      ++m_nRefCount; 
   } 
   void Release() {       
      --m_nRefCount; 
      if (0 == m_nRefCount) { 
        delete this; 
      } 
   } 
   // ... 
}

Listing 7.26    Zählen von Referenzen (Das aufgeführte Listing ist vereinfacht dargestellt. Die komplette Implementierung dieses Verfahrens finden Sie auf der Webseite zum Buch, www.objektorientierte-programmierung.de.)

In Zeile wird ein Konstruktor definiert, dem ein Exemplar von RefCounted übergeben wird. Dabei wird auf ihm auch gleich der Referenzzähler erhöht, da es nun eine weitere Referenz auf das Objekt über den gerade konstruierten neuen Smart Pointer gibt. In Zeile erfolgt das Gleiche für den Konstruktor, in dem eine Kopie angelegt wird. In Zeile wird der Zähler ebenfalls heruntergezählt, weil gerade einer der Smart Pointer gelöscht wird. In Zeile wird ein neues Exemplar von RefCounted auf den Smart Pointer zugewiesen. Deshalb muss der Zähler für das bisher referenzierte Objekt heruntergezählt werden, für das neue Objekt erhöht (Zeilen mit der Markierung ).

Für die Klasse RefCounted sind die Operationen zum AddRef und Release umgesetzt. Dabei zählt AddRef in Zeile ganz einfach den Zähler hoch. Release (Zeile ) setzt den Zähler herunter und gibt das Objekt frei, sobald der Zählerstand 0 erreicht.

Problem: Zyklische Referenzen

So weit, so einfach. Allerdings hat dieses Verfahren einen großen Haken, der es für den Einsatz in vielen Fällen unbrauchbar macht: Wenn irgendwo in unseren Objekten zyklische Referenzen auftauchen, werden die betroffenen Objekte nie den Zählerstand 0 erreichen und damit auch nie gelöscht werden. In diesem Fall gibt es nämlich auf jedes der beteiligten Objekte immer eine Referenz. Zyklische Referenzen sind aber in objektorientierten Anwendungen etwas sehr Normales und häufig.

Abbildung 7.23    Zyklische Verweise auf Objekte

In Abbildung 7.23 sind solche zyklischen Verweise dargestellt. Auftreten können sie zum Beispiel, wenn Rückreferenzen bei Aggregationsbeziehungen gepflegt werden. Dabei hält das Aggregat eine Liste der aggregierten Objekte, diese wiederum halten eine Referenz auf das Aggregat. Diese Information ist dann zwar redundant, kann aber aus Gründen der Zugriffseffizienz sinnvoll sein. Im dargestellten Beispiel kennt ein Auftrag die Liste der in ihm enthaltenen Positionen, umgekehrt weiß aber auch jede Position, zu welchem Auftrag sie gehört.

Zählen von Referenzen ist nicht vollständig.

Was wir aber brauchen, ist ein Verfahren, das konsistent und vollständig ist. Konsistent heißt in diesem Fall: Es wird nie ein Objekt aufgeräumt, das noch gebraucht wird. Diese Anforderung erfüllt das Zählen von Referenzen. Vollständig heißt in diesem Fall: Es werden alle Objekte, die nicht mehr gebraucht werden, auch wirklich weggeräumt. Diese Anforderung wird durch das Zählen von Referenzen nicht erfüllt.

Allerdings kann das Zählen von Referenzen für Teile einer Applikation, in denen zyklische Referenzen nicht erwartet werden, eine einfache und ressourcenschonende Methode sein, um Speicher automatisch zu verwalten.

So verwenden einige Klassen aus der C++-Standardbibliothek Referenzzähler, um ein automatisches Löschen von Objekten zu ermöglichen. Ein Beispiel dafür ist die string-Klasse.

Markieren und Löschen (Mark and Sweep)

Wenn also die Möglichkeit von zyklischen Referenzen besteht, ist das reine Mitzählen nicht ausreichend. In diesem Fall müssen alternative Verfahren zum Einsatz kommen. Eine gängige Alternative ist das sogenannte Mark-and-Sweep-Verfahren. Wörtlich übersetzt heißt das Verfahren Markieren und Ausfegen.

Markierungsphase

Der Mark-and-Sweep-Algorithmus sucht zunächst alle Objekte, die von anderen referenziert werden, und markiert sie (Markierungsphase). Dazu ist es notwendig, dass ein Startpunkt (möglicherweise auch mehrere Startpunkte) bekannt ist, also Objekte, von denen wir wissen, dass sie nicht entfernt werden dürfen. [Existiert ein solcher Startpunkt nicht, können alle Objekte entfernt werden. Diese Situation tritt aber in praktischen Fällen nicht auf, da in der Regel ja zumindest mit der Applikation selbst (die in der Regel auch ein Objekt ist) weitergearbeitet werden soll. ] Zu solchen Objekten gehört zum Beispiel das Applikationsobjekt selbst. Diese Startobjekte werden nun markiert, also in eine Liste von Objekten aufgenommen, die nicht gelöscht werden dürfen. Welche Objekte werden aber nun wiederum von den Startobjekten referenziert? Sie alle müssen wir natürlich auch in die Liste aufnehmen, sofern sie nicht schon dort enthalten sind. Und alle neu hinzugefügten Objekte müssen ebenfalls überprüft werden.

Die Suche bedient sich dabei Algorithmen, die zur Lösung des Erreichbarkeitsproblems in Graphen verwendet werden.

In Abbildung 7.24 ist die Markierungsphase für ein Beispiel dargestellt. Ausgehend vom Applikationsobjekt werden alle erreichbaren Objekte markiert. Die fett gezeichneten Linien werden dabei durchlaufen, so dass am Ende die dunkelgrau hinterlegten Objekte markiert sind. Bereits markierte Objekte (wie zum Beispiel Auftrag2) werden nicht mehr angepasst und weiter bearbeitet, wenn sie zum zweiten Mal erreicht werden. Die hellgrau hinterlegten Objekte sind nicht erreichbar und werden somit auch nicht markiert.

Abbildung 7.24    Markieren von erreichbaren Objekten

Löschphase

Ist die Markierungsphase abgeschlossen, folgt die sogenannte Sweep-Phase. Dabei findet ein erneuter Durchlauf durch die Objektmenge statt, bei dem jedes Objekt, das nicht markiert wurde, gelöscht wird, da es erwiesenermaßen nicht mehr referenziert wird.

Abbildung 7.25    Verbleibende Objekte nach Sweep

In Abbildung 7.25 sind die nach der Sweep-Phase verbleibenden Objekte dargestellt. [Der englische Begriff sweep hat die wörtliche Übersetzung »ausfegen«, was schon ziemlich gut das Vorgehen des Algorithmus trifft. Wir sprechen trotzdem in der deutschen Übersetzung von der Löschphase des Algorithmus. ] Beim Löschdurchlauf wird außerdem das Flag zurückgesetzt, das die Objekte als bereits markiert gekennzeichnet hat.

Komplexität abhängig von Gesamtzahl der Objekte

Mark and Sweep hat allerdings ein paar Nachteile. Der Hauptnachteil ist die Komplexität des zugehörigen Algorithmus, also sein Bedarf an Zeit und Speicher. Der Zeitbedarf der Markierungsphase ist linear abhängig von der Anzahl der noch verwendeten Objekte. Der Zeitbedarf der Löschphase ist linear abhängig von der Anzahl der insgesamt vorhandenen Objekte.

Kopieren der erreichbaren Objekte

In vielen Fällen besser als Mark and Sweep arbeitet ein anderer Algorithmus (Copy-Collector), der die noch erreichbaren Objekte direkt in einen neuen Speicherbereich kopiert. Da der Algorithmus die nicht erreichbaren Objekte überhaupt nicht betrachten muss, ist seine Komplexität nur von der Anzahl der wirklich erreichbaren Objekte abhängig. In sehr dynamischen Anwendungen, in denen eine große Zahl von kurzlebigen Objekten erzeugt wird, kann die Anzahl der zu löschenden Objekte im Vergleich zu der Zahl der überlebenden Objekte sehr groß sein, der Vorteil des Algorithmus kann also relevant sein.

Beim Kopieren wird der komplette noch erreichbare Bestand an Objekten in einen vorher leeren Speicherbereich kopiert, den sogenannten To-Space. Danach wird der bisher genutzte Speicherbereich, der auch From-Space genannt wird, geleert. Die genutzten Speicherbereiche werden also bei jeder durchgeführten Garbage Collection vertauscht.

Aktionen

Dabei werden für die betroffenen Objekte jeweils die folgenden Aktionen durchgeführt:

  • Das betrachtete Objekt wird in den To-Space kopiert.
  • Der Zeiger, über den das Objekt erreicht wurde, wird umgesetzt und auf dessen neue Position gesetzt.
  • Das ursprüngliche Objekt im From-Space erhält einen Verweis auf seine neue Inkarnation im To-Space.

Zwischenstand beim Kopieren

In Abbildung 7.26 ist der Stand des Kopierens dargestellt, nachdem das Einstiegsobjekt (das Applikationsobjekt) kopiert und der Zeiger darauf umgesetzt wurde. Die von diesem Objekt aus weiterführenden Referenzen sind noch nicht angepasst und zeigen immer noch in den From-Space.

Kopieren von referenzierten Objekten

In Abbildung 7.27 ist der nächste Schritt dargestellt, bei dem dann die ersten beiden referenzierten Objekte kopiert wurden. Wieder sind deren weitere Referenzen noch in den From-Space gerichtet.

Abbildung 7.26    Start des Kopierens in den To-Space

Abbildung 7.27    Kopieren von referenzierten Objekten

Ganz einfach wird das Verfahren, wenn ein Objekt bereits kopiert wurde. In Abbildung 7.28 ist die Situation dargestellt, dass das bereits kopierte Objekt Position eine Referenz auf das bereits früher kopierte Objekt Auftrag2 hat.

Abbildung 7.28    Referenz auf bereits kopiertes Objekt

Referenzen umsetzen

In diesem Fall ist nichts weiter zu tun, als die Referenz, die von Position aus gehalten wird, auf das bereits kopierte Objekt zu setzen. Wo dieses liegt, haben wir uns im ursprünglichen Objekt Auftrag2 vorher gemerkt.

Entfernen

Nach Abschluss des Verfahrens sind die noch erreichbaren Objekte im To-Space gelandet, wie in Abbildung 7.29 dargestellt. Der gesamte Speicherbereich im From-Space wird nun auf einen Schwung freigegeben, da alle noch benötigten Objekte bereits kopiert wurden. Die Applikation kann jetzt mit allen noch in Verwendung befindlichen Objekten weiterarbeiten.

Abbildung 7.29    Zustand nach Abschluss der Garbage Collection

Die junge und die alte Generation (Generational Garbage Collection)

In der Praxis machen die meisten modernen Verfahren zur Garbage Collection die Tatsache nutzbar, dass die Lebensdauer von Objekten nicht gleichmäßig verteilt ist.

Die Speicherverwaltung der virtuellen Maschine von Java nutzt unter anderem auch ein Verfahren, das die Speicherbereinigung vom Alter der betroffenen Objekte abhängig macht. Die Grundidee dabei ist: Wenn ein Objekt die erste Zeit überstanden hat, dann wird es sehr wahrscheinlich auch weiter dabei bleiben. Es macht also Sinn, zwischen jungen und alten Objekten zu unterscheiden.

Ältere Objekte bleiben unter sich.

Und eine weitere Beobachtung: Ältere Objekte bleiben meist unter sich, sie referenzieren nur selten junge Objekte. Der Dialog zwischen den Generationen findet bei Objekten nur sehr begrenzt statt. Diese Beobachtungen sind für den überwiegenden Teil von Applikationen zutreffend.

Wir geben hier einen kurzen Überblick über das Verfahren, wie es von der virtuellen Maschine von Java (Hotspot) angewendet wird.

Abbildung 7.30    Junge und alte Generation von Objekten

Verwendete Speicherbereiche

In Abbildung 7.30 sind die verwendeten Speicherbereiche jeweils mit typischen Bewohnern abgebildet:

  • Neue Objekte werden in einem Bereich angelegt, der Eden genannt wird. Hier gibt es so kurzlebige Objekte wie Iteratoren oder temporäre Objekte, die nur innerhalb einer Methode verwendet werden. Aber auch langlebige Objekte werden zunächst einmal hier angelegt.
  • Es gibt zwei weitere Bereiche für relativ junge Objekte, die für das bereits beschriebene Verfahren der kopierenden Garbage Collection in From-Space und To-Space eingeteilt sind. Hierhin schaffen es nur Objekte, die eine etwas längere Lebensdauer haben, deshalb wird der Bereich auch Survivor Space genannt. Hier liegen zum Beispiel fachliche Objekte. Im Beispiel liegen dort auch ein Auftrag und eine Auftragsposition.
  • Schließlich gibt es noch den Bereich für die alte Generation von Objekten, die bereits einige Garbage Collections überstanden haben. Hier landen langlebige Objekte, wie etwa ständig benötigte Fenster einer Applikation, Objekte für Datenbankverbindungen oder auch Aufzählungen, die von der Applikation ständig benötigt werden. Dieser wird auch Old-Generation-Space genannt.

Es finden nun zwei ganz verschiedene Arten der Garbage Collection statt. Der Unterschied zwischen den beiden ist etwa der zwischen dem Aufräumen unserer Wohnung und dem kompletten Entrümpeln.

Aufräumen

Die erste Variante, das Aufräumen, wird häufiger durchgeführt und arbeitet nur auf dem Bereich der jungen Generation. Wir sprechen dabei von einer kleinen (minor) Garbage Collection. Dabei wird standardmäßig nach dem Copy-Verfahren aus dem Bereich Eden und dem From-Space in den To-Space kopiert.

Für die Objekte, die aus dem From-Space kopiert werden, wird zusätzlich ein Zähler hochgezählt. Erreicht dieser einen vordefinierten Wert, haben die Objekte also eine größere Zahl von Durchläufen des Garbage Collectors überstanden, dann dürfen sie sich zur alten Generation gesellen und werden in deren Bereich verschoben.

Entrümpeln

Ein komplettes Entrümpeln führen wir ja auch im realen Leben eher selten durch, weil das wesentlich aufwändiger ist. So wird auch Garbage Collection im Bereich der alten Generation nur in größeren Abständen durchgeführt. Diese wird dann als große (major) Garbage Collection bezeichnet.

So viele Algorithmen und Verfahren: Was geht mich das an?

Sofern in unserem gewählten Entwicklungssystem eine gute Garbage Collection zur Verfügung steht, werden wir zumindest kein eigenes Verfahren dafür umsetzen müssen. Das Vorhandensein von erprobten Verfahren zur Garbage Collection ist denn auch ein gutes Argument für die Verwendung einer Sprache wie Java anstelle von C++.

Allerdings bietet zum Beispiel die Java-Laufzeitumgebung seit der Version 1.4 gleich vier verschiedene Verfahren zur Garbage Collection an, die alle auch über verschiedene Parameter weiter beeinflusst werden können. Das voreingestellte Verfahren wird zwar für viele Fälle funktionieren, um die Effizienz einer Applikation zu steigern, ist ein gezielter Einsatz der speziellen Verfahren aber ein gutes Mittel.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

 <<   zurück
  Zum Rheinwerk-Shop
Neuauflage: Objektorientierte Programmierung






Neuauflage:
Objektorientierte Programmierung

Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: Einstieg in Python






 Einstieg in Python


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern