next up previous
Nächste Seite: Fundamentale Objekt-Eigenschaften Aufwärts: Objekt-Modelle von Bibliotheken Vorherige Seite: Objekt-Modelle von Bibliotheken

Lebenszyklus von Objekten

Abbildung 5.1: Von Resources vorgegebener Lebenszyklus
\begin{figure}\epsfig{file=resources.eps}\end{figure}

Der zunächst wichtigste Umgang mit Objekten umfaßt deren Lebenszyklus von der Allokation des zugehörigen Speichers bis zu dessen Freigabe. Typischerweise erfolgt die Allokation implizit (z.B. Eiffel und C++) oder explizit (z.B. Oberon) durch einen Konstruktor. Die Mehrheit der objekt-orientierten Programmiersprachen (eine prominente Ausnahme ist C++) gibt dann den für das Objekt allokierten Speicher wieder vollautomatisch frei, wenn es nicht mehr möglich ist, darauf zuzugreifen (garbage collection).

Doch gerade das Ableben eines Objekts kann sich etwas aufwendiger darstellen. In Eiffel und einigen anderen Programmiersprachen ist es möglich, ein Objekt, das gerade deallokiert werden soll, noch einige Aktionen ausführen zu lassen (z.B. die Freigabe externer Ressourcen wie offene Dateiverbindungen). Dies hat die Konsequenz, dass sich das Objekt potentiell wieder in lebende Strukturen einhängen kann und diese ``Attacke'' überlebt. Wegen dieser Komplexität, die die garbage collection nicht unerheblich komplizierter macht bzw. in ihren Möglichkeiten einschränkt, steht diese Möglichkeit auch nicht überall zur Verfügung.

Einige Systeme unterscheiden zwischen heavyweight und lightweight Zeigern, wobei das Überleben eines Objektes nur von den heavyweight Zeigern abhängt. Wenn ein Objekt dann automatisch deallokiert wird, werden alle entsprechenden lightweight Zeiger auf NIL gesetzt. Dies ist dann nützlich, wenn Objekte in eine Reihe von buchhalterischen Datenstrukturen enthalten sind, die die Lebensdauer nicht beeinflussen sollen.

Bei Oberon gibt es von der Programmiersprache her nur die Unterstützung für die garbage collection, jedoch nicht für Operationen kurz vor der Deallokation oder die Unterscheidung von Verweisen. Dessen ungeachtet ist es möglich, diese Mechanismen bei Bedarf über die Bibliothek zu realisieren - in der Ulmer Oberon-Bibliothek geschieht dies durch das Modul Resources, das in Bezug auf den Lebenszyklus einen kooperativen Umgang mit Objekten erlaubt.

Jedes Objekt, das eine Erweiterung von Disciplines.Object ist, befindet sich in einem der fünf folgenden Zustände, die von Resources definiert werden (siehe Abbildung 5.1):

alive
Dies ist der Anfangszustand aller Objekte, der solange bleibt, wie das Objekt für Operationen zur Verfügung steht und schwergewichtige Zeiger darauf verweisen.5.2

unreferenced
Dieser Zustand wird erreicht, wenn der letzte der schwergewichtigen Zeiger auf das Objekt entfernt wird. In dieser Situation können noch Aufräumarbeiten durchgeführt werden, die insbesondere die leichtgewichtigen Zeiger kappen sollten, damit die garbage collection das Objekt aufräumen kann.

terminated
ist der finale Zustand, wenn ein Objekt nicht mehr zu benutzen ist. Ggf. sind letzte Verweise (egal welcher Art) darauf wegzunehmen, damit es der garbage collection anheim fallen kann.

stopped & alive
Dieser Zustand tritt ein, wenn das Objekt zwar noch lebt und in allen Datenstrukturen verbleiben sollte, jedoch eine Kommunikation zur Zeit nicht möglich ist. Damit ist der Effekt zwischenzeitlich aufgerufener Operationen undefiniert (möglicherweise werden sie ignoriert, aufgesammelt oder blockieren den Aufrufer, bis das Objekt wieder ansprechbar ist).

stopped & unreferenced
kann theoretisch ebenfalls eintreten, wenn während der Zeit des Aufräumens die Verbindung temporär abbricht.

Leichte Zeiger werden auf gewohnte Weise einfach durch entsprechende Zuweisungen gesetzt, wobei nur ggf. die Verpflichtung entsteht, bei einem Übergang zu unreferenced oder terminated die Verbindungen zu kappen. Schwergewichtige Zeiger werden zusätzlich durch Resources.Attach und Resources.Detach begleitet, die über eine private Disziplin die Anzahl der schwergewichtigen Verweise hoch- und runterzählen. Landet der Zähler wieder auf 0, so wird automatisch der Zustandswechsel nach dereferenced ausgelöst. Ähnliche Referenz-Zähler gibt es auch in einigen C-Bibliotheken, die auf diese kooperative Weise dann feststellen, wann ein Destruktor aufgerufen werden kann. Bei Oberon ist dies Verfahren allerdings nur notwendig, wenn wirklich für ein Objekt beide Zeigervarianten verwendet werden. Grundsätzlich haben natürlich alle Referenz-Zähler-Implementierungen den Nachteil, daß sie bei zyklischen Datenstrukturen nicht helfen. Ein anderes Problem besteht in der Möglichkeit der nicht korrekten Verwendung von Resources.Attach und Resources.Detach, die in Oberon jedoch durch die Verwendung von Schlüsseln zuverlässig verhindert wird.

Die Unterscheidung zwischen lebenden und toten Objekten nicht nur auf Basis der Erreichbarkeit im Sinne der garbage collection bringt enorme Vorteile im Bereich verteilter Systeme, da dann ein flexibler und allgemeiner Mechanismus zur Verfügung steht, der bei einem Zusammenbrechen von Verbindungen (oder deren temporären Ausfall) eingesetzt werden kann.


next up previous
Nächste Seite: Fundamentale Objekt-Eigenschaften Aufwärts: Objekt-Modelle von Bibliotheken Vorherige Seite: Objekt-Modelle von Bibliotheken
Andreas Borchert 2000-12-18