Institut für Technische Informatik (TeI) Professur für VLSI-Entwurfssysteme, Diagnostik und Architektur

Ähnliche Dokumente
Multi-Port-Speichermanager für die Java-Plattform SHAP

Java-Bytecode-Prozessor SHAP

Early first draft Höllische Programmiersprachen Seminar im WS 2014/15 Speichermanagement

Simulative Verifikation und Evaluation des Speichermanagements einer Multi-Core-Prozessorarchitektur am Beispiel von SHAP

Concurrent Garbage Collector

Programmiertechnik II

Vorlesung Programmieren

Software Entwicklung 1

Agenda. Informatik I WS05/06 Folien von Tobias Dezulian

Simulative Verifikation und Evaluation des Speichermanagements einer Multi-Core-Prozessorarchitektur am Beispiel von SHAP

Speichermanagement Software Entwicklung 1

Speichermanagement Software Entwicklung 1

Programmieren in Java

Was ist Reference Counting Implementierung. Ende. Reference Counting. Kevin Köster. Uni Hamburg. 31. März Kevin Köster Reference Counting 1/58

Einstieg in die Informatik mit Java

OpenCL. Programmiersprachen im Multicore-Zeitalter. Tim Wiersdörfer

Organisatorisches. Folien (u.a.) auf der Lva-Homepage Skriptum über MU Online

Speicherverwaltung in C

Mehrdimensionale Arrays

Java Garbage Collector: Funktionsweise und Optimierung. Mathias Dolag Prof. Dr. Peter Mandl (DOAG 2012, )

Grundlagen der OO- Programmierung in C#

C++ - Objektorientierte Programmierung Konstante und statische Elemente

GERICHTETER GEWICHTETER GRAPH DESIGNDOKUMENT

Objektorientierte Programmierung und Klassen

Schlussendlich geben wir die Listen aus. Es kommt zu folgender Ausgabe:

Einstieg in die Informatik mit Java

C-to-CUDA-Compiler. Johannes Kölsch. October 29, 2012

Informatik - Übungsstunde

Java: Kapitel 1. Überblick. Programmentwicklung WS 2008/2009. Holger Röder Holger Röder

Microsoft.NET Framework & Component Object Model. ein Vortrag von Florian Steuber

7. Übung Informatik II - Objektorientierte Programmierung

Crashkurs C++ - Teil 1

Algorithmen und Datenstrukturen II

Informatik II SS Inhalt. Objektlebensdauer (2/3) Objektlebensdauer (1/3)

Cell and Larrabee Microarchitecture

Gliederung. Algorithmen und Datenstrukturen II. Java: Objektorientierung. Java: Objektorientierung. Objektorientierung in JAVA. D.

Programmierkurs. Steffen Müthing. January 18, Interdisciplinary Center for Scientific Computing, Heidelberg University

Objektorientierte Programmierung II

Programmieren I. Kapitel 12. Referenzen

HSR Rapperswil 2001 Markus Rigling. Programmieren: Vererbung. 1 Variante 2

Garbage Collection. Wahlpflichtfach Compilerbau. Student: WS 2009/2010. Christian Funk, ,

FH D. Objektorientierte Programmierung in Java FH D FH D. Prof. Dr. Ing. André Stuhlsatz. Referenzen. Referenzen

Organisatorisches. Folien (u.a.) gibt's auf der Lva-Homepage zum Download

Seminar: Multi-Core Architectures and Programming

7 Laufzeit-Speicherverwaltung

Vererbung. Gerd Bohlender. Institut für Angewandte und Numerische Mathematik. Vorlesung: Einstieg in die Informatik mit Java 23.5.

Memory Models Frederik Zipp

Einführung in die Programmiersprache Java II

Einführung in die Programmierung

Wie groß ist die Page Table?

Architekturen, Werkzeuge und Laufzeitumgebungen für eingebettete Systeme

Languages and Tools for Object-Oriented Development Klausur Wintersemester 2007/2008

Hardware und Gerätetreiber

Vererbung, Polymorphie

FPGA Systementwurf. Rosbeh Etemadi. Paderborn University. 29. Mai 2007

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Automatisches Speichermanagement

Algorithmen und Datenstrukturen

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Java als erste. Programmiersprache. Java 2 Plattform. Von Prof. Dr. Joachim Goll Cornelia Weiß Peter Rothländer. 2., durchgesehene Auflage

Prinzipien der objektorientierten Programmierung (OOP)

Theorie zu Übung 8 Implementierung in Java

Informatik II Übung 7 Gruppe 7

Lokale Strategien zur Garbage Collection Problemstellung der verteilten Gargabe Collection Algorithmen

Objekte. Theorieteil. Inhaltsverzeichnis. Begriffe. Programmieren mit Java Modul 5. 1 Modulübersicht 3

Nebenläufige Programmierung in Java: Threads

Physikalisch Technische Lehranstalt Wedel 31. Januar 2004 Prof. Dr. Uwe Schmidt

Betriebssysteme Vorstellung

Dynamische Speicherverwaltung

6 Speicherorganisation

Grundlagen. Felix Döring, Felix Wittwer 24. April Python-Kurs

Institut für Programmierung und Reaktive Systeme. Java 6. Markus Reschke

Praktische Informatik 1

Polymorphie und UML Klassendiagramme

Universität Hamburg. Seminar: Effiziente Programmierung in C. Referenzzählung. Autoren: Kevin Köster. Betreuer: Michael Kuhn

Proseminar: Konzepte von Betriebsystem-Komponenten (KVBK)

C++ - Objektorientierte Programmierung Vererbung

Dynamische Datentypen. Destruktor, Copy-Konstruktor, Zuweisungsoperator, Dynamischer Datentyp, Vektoren

Durch die Teil-von-Beziehung soll ausgedrückt werden, dass ein Objekt A als (physikalischer) Teil eines Objekts B angesehen wird. Insbesondere kann ei

Java Referenzdatentypen genauer betrachtet

Vererbung und Polymorphie

Innere Klassen. Gerd Bohlender. Institut für Angewandte und Numerische Mathematik. Vorlesung: Einstieg in die Informatik mit Java

Betriebssysteme KU - Einführungstutorium

Fachbereich Medienproduktion

Realisierung eines Speichermanagements zur Zugriffsvirtualisierung von konkurrierenden Nutzerdesigns auf Rekonfigurierbarer Hardware

DIPLOMARBEIT. Entwurf und Implementierung eines modularen USB-Stacks für eingebettete Controller ohne Betriebssystem. Uwe Pfeiffer

Verteilte Systeme - Java Networking (Sockets) 2 -

Compute Unified Device Architecture CUDA

ASIC-SYNTHESE DER SHAP-MIKROARCHITEKTUR

6 Speicherorganisation

Java als erste Programmiersprache

Einstieg in die Informatik mit Java

Einstieg in die Informatik mit Java

Verschlüsseln eines Bildes. Visuelle Kryptographie. Verschlüsseln eines Bildes. Verschlüsseln eines Bildes

Algorithmen und Datenstrukturen 07

DAP2-Programmierpraktikum Einführung in C++ (Teil 2)

Javakurs für Fortgeschrittene

Transkript:

e TeI 1D C1 =1 = T 1D C1 I TECHNISCHE UNIVERSITÄT DRESDEN FAKULTÄT INFORMATIK Institut für Technische Informatik (TeI) Professur für VLSI-Entwurfssysteme, Diagnostik und Architektur DIPLOMARBEIT Realisierung einer integrierten Speicherverwaltung mit der Unterstützung schwacher Referenzen für den Prozessor SHAP Vorgelegt von: Peter Reichel Geboren am: 31.07.1983 in: Freital zum Erlangen des akademischen Grades DIPLOMINGENIEUR (Dipl.-Ing.) Verantwortlicher Hochschullehrer: Prof. Dr.-Ing. habil. R. G. Spallek Betreuer: Dipl.-Inf. Thomas Preußer Datum: 31. August 2008

Selbstständigkeitserklärung Hiermit erkläre ich, dass ich die von mir am heutigen Tage dem Prüfungsausschuss der Fakultät Elektrotechnik und Informationstechnik eingereichte Diplomarbeit zum Thema Realisierung einer integrierten Speicherverwaltung mit der Unterstützung schwacher Referenzen für den Prozessor SHAP vollkommen selbstständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt sowie Zitate kenntlich gemacht habe. Dresden, den 31. August 2008

Kurzfassung In dieser Arbeit wird der Entwurf einer durch Hardware unterstützten, automatischen Speicherverwaltung für den Java-Bytecode-Prozessor SHAP vorgestellt. Mit Hilfe erweiterter Phasen des Objektlebenszyklus in Form von schwachen Referenzen können Abschlussaktionen für Objekte implementiert werden. Der nebenläufige Garbage Collector nutzt einen eigenen Mikroprozessor und begrenzt sowohl Anzahl als auch Dauer der notwendigen Unterbrechungen der Anwendung auf ein Minimum. Zusätzliche in Hardware implementierte Routinen unter anderem zum Scannen oder Verschieben von Objekten dienen der Beschleunigung der Garbage Collection. Auch die mögliche Erweiterung hinsichtlich der Unterstützung mehrerer SHAP-Cores wird vorbereitet. Zunächst erfolgt eine Betrachung möglicher erweiterter Phasen des Objektlebenszyklus sowie deren Realisierung in verschiedenen Umgebungen wie der JamVM, Microsoft.NET, der KVM von SUN Microsystems sowie der Skript-Sprache Ruby. Außerdem werden verschiedene bestehende Systeme mit hardwareunterstützter automatischer Speicherverwaltung betrachtet. Der Entwurf des erweiterten Collectors beginnt mit einer Untersuchung der Möglichkeiten zur exakten Unterscheidung von Referenzen und Daten auf dem Heap. Daraufhin wird die Möglichkeit der Steuerung mit Hilfe eines Mikroprozessors diskutiert und mögliche Maßnahmen zur Unterstützung von mehreren SHAP-Cores erarbeitet. Die daraus resultierenden Besonderheiten des vollständigen GC-Zyklus werden betrachtet und verschiedene Konzepte zur Unterstützung schwacher Referenzen formuliert. Zur Auswertung kommen verschiedene Test-Applikationen zum Einsatz, welche sowohl zum Funktionsnachweis als auch für Untersuchungen der Leistungsfähigkeit des implementierten Systems eingesetzt werden.

Summary In this thesis, the design of a hardware supported automatic memory management system for the Java bytecode processor SHAP is introduced. By using extended phases of the object life cycle, finalization routines for objects can be implemented. The concurrent garbage collector is using an own microprocessor and limits the count as well as the duration of necessary interruptions of the application to a minimum. Additionally, hardware-implemented routines, such as the scan or the movement of a objects, serve the acceleration of the garbage collection. Also, the possible extension concerning the support for multiple SHAP cores is prepared. First of all, possible extended phases of the object life cycle and their realization within the JamVM, Microsoft.NET, the KVM from Sun Microsystems and the scripting language Ruby are analyzed. Also, existing systems with hardware support for garbage collection are surveyed. The design of the extended collector starts with the analysis of the possibilities for exact distinction between references and primitive data on the heap. Then, the potential of using a microprocessor for controlling is discussed and possible arrangements to support multiple SHAP cores are developed. The resulting characteristics of the complete garbage collection cycle are examined and different concepts for supporting weak references are drafted. For evaluation, different test applications for operation verification as well as for performance checking of the implemented system are used.

Inhaltsverzeichnis 1 Einleitung 1 2 Stand der Technik 3 2.1 Garbage Collection.............................. 3 2.1.1 Allgemein............................... 3 2.1.2 Zählung der Referenzen....................... 3 2.1.3 Traversieren des Objektgraphen................... 3 2.2 Objektlebenszyklus.............................. 5 2.2.1 Allgemeiner Objektlebenszyklus................... 5 2.2.2 Erweiterte Phasen des Objektlebenszyklus.............. 6 2.3 Erweiterter Objektlebenszyklus in Java.................... 8 2.3.1 Finalizer............................... 8 2.3.2 Schwache Referenzen........................ 8 2.4 SHAP..................................... 11 2.4.1 Aufbau der Architektur........................ 12 2.4.2 Memory Management Unit...................... 13 2.4.3 Weiterentwicklung.......................... 14 2.5 Implementierung schwacher Referenzen in bestehenden Systemen..... 15 2.5.1 JamVM................................ 15 2.5.2 Sun KVM............................... 16 2.5.3 Microsoft.NET............................ 18 2.5.4 Ruby................................. 20 2.6 Hardwareunterstützte Garbage Collection.................. 21 2.6.1 Systeme mit Tracing-GC....................... 21 2.6.2 Systeme mit Reference-Counting-GC................ 31 3 Entwurf 33 3.1 Zielstellung.................................. 33 3.2 Unterscheidung zwischen Referenzen und Daten.............. 34 3.2.1 Exakter Heapscan........................... 34 3.2.2 Nichtinitialisierte Objekte...................... 34 3.3 Steuerung durch Mikroprozessor....................... 35 3.3.1 Motivation.............................. 35 3.3.2 Memory Control Unit........................ 35 3.4 Unterstützung für mehrere SHAP-Cores................... 39 3.4.1 Probleme des bisherigen Ansatzes.................. 39

IV Inhaltsverzeichnis 3.4.2 Markierung erreichter Objekte.................... 39 3.4.3 Synchronisation mit dem Mutator.................. 40 3.4.4 GC-Bus................................ 42 3.5 Segmentverwaltung.............................. 43 3.5.1 Realisierung mit MCU........................ 43 3.5.2 Bereitstellung des Allokationssegments............... 44 3.6 GC-Konzept.................................. 45 3.6.1 Allgemeiner Ansatz......................... 45 3.6.2 Objektzustände und Erreichbarkeit.................. 45 3.6.3 Durchführung des Heapscans.................... 48 3.6.4 Ablauf der Garbage Collection.................... 50 3.6.5 Unterstützung mehrerer Generationen................ 51 3.7 Konzepte zur Unterstützung erweiterter Phasen des Objektlebenszyklus.. 51 3.7.1 Unterstützte Phasen.......................... 51 3.7.2 Techniken............................... 52 3.7.3 Gewähltes Konzept.......................... 61 4 Auswertung 63 4.1 Implementierung............................... 63 4.1.1 Auswahl eines MCU-Prozessors................... 63 4.1.2 Struktur des Systems......................... 63 4.1.3 MCU Programm........................... 64 4.1.4 Vergleich der Hardware-Auslastung................. 65 4.2 Funktionsnachweis des neuen GC-Konzepts................. 65 4.2.1 Test-Applikationen.......................... 65 4.2.2 Stabilitätstests............................ 66 4.3 Erweiterter Objektlebenszyklus........................ 66 4.3.1 Nachweis des korrekten Verhaltens................. 66 4.3.2 Einfluss auf Phasen der Garbage Collection............. 68 4.3.3 Verhalten bei SoftReferences..................... 69 4.4 Schreib-Barriere................................ 70 4.4.1 Operationen zum Speicherzugriff.................. 70 4.4.2 Verursachter Overhead........................ 70 5 Zusammenfassung und Ausblick 73 A Quellcodes 75 B Inhalt der CD-ROM 81

Abkürzungsverzeichnis API................ CDC............... CLDC.............. CLR............... FPGA.............. FSM............... GC................ GCC............... GCMM............ IO................. ISA................ J2ME.............. J2SE............... JOP................ JVM............... MCU.............. MMU.............. MUX.............. RAM.............. RISC............... ROM............... S3SK.............. SHAP.............. VHDL............. Application Programming Interface Connected Device Configuration Connected Limited Device Configuration Common Language Runtime Field Programmable Gate Array Finite State Machine Garbage Collector GNU Compiler Collection Garbage Collected Memory Module Input Output Instruction Set Architecture Java Platform 2, Micro Edition Java 2 Platform, Standard Edition Java Optimized Processor Java Virtual Machine Memory Control Unit Memory Management Unit Multiplexer Random Access Memory Reduced Instruction Set Computing Read Only Memory Spartan-3-Starterkit Secure Hardware Agent Platform Very High Speed Integrated Circuit Hardware Description Language

Abbildungsverzeichnis 2.1 Darstellung des einfachen Objektlebenszyklus................ 5 2.2 Darstellung des erweiterten Objektlebenszyklus mit Finalizern........ 6 2.3 Referenz- oder Proxy-Objekte zur Realisierung schwacher Referenzen... 7 2.4 Darstellung des erweiterten Objektlebenzyklus mit schwachen Referenzen. 7 2.5 Klassen des Pakets java.lang.ref.................... 9 2.6 Erzeugung einer schwachen Referenz in Java................. 10 2.7 Übersicht über die SHAP-Mikroarchitektur (aus [Zab + 07]).......... 12 2.8 Übersicht über die Struktur der MMU der SHAP-Mikroarchitektur...... 13 2.9 Darstellung der Garbage Collection in der JamVM.............. 17 2.10 Garbage Collection im CLR der Microsoft.NET Plattform.......... 20 2.11 Integration des GCMM in ein Computersystem................ 23 2.12 Speicherzustand im Active Memory Processor (nach [Sri + 00])........ 24 2.13 Freigabe von Speicherbereichen im Active Memory Processor (nach [Sri + 00]). 25 2.14 RPA-Query für die Schreib-Barriere des Concurrent GCs (aus [HeSm00]).. 26 2.15 Von Meyer et al. entworfene Prozessorarchitektur (nach [Meye04])..... 28 2.16 Phasen der Garbage Collection in der SHAP-Plattform (aus [Reic07]).... 31 2.17 Struktur des Garbage Collectors der SHAP-Plattform (aus [Reic07]).... 32 3.1 Darstellung des bidirektionalen Objektlayouts................. 35 3.2 Einbindung der MCU in das System über die IO-Schnittstellen........ 38 3.3 Komponente zur dezentralen Bestimmung des Root Sets........... 40 3.4 Verhalten der Schreib-Barriere beim Überschreiben einer Referenz..... 41 3.5 Stuktur des GC-Busses............................. 42 3.6 Bereitstellung mehrerer Allokationssegmente mit Hilfe zweier FIFOs.... 44 3.7 Durch den Heapscan zugewiesene Markierung von Objekten......... 46 3.8 Darstellung der möglichen Zustände eines Objektes sowie deren Übergänge. 47 3.9 Darstellung des Scan Termination Controllers................. 49 3.10 Zu unterstützende Erreichbarkeitszustände in der SHAP-Plattform...... 52 3.11 Aufbau einer Referenz in der SHAP-Plattform................ 54 3.12 Zuordnung der Reference-Objekte zu ihrem Referent........... 56 3.13 Globale Liste aller vorhandenen Objekte von Reference.......... 57 3.14 Ablauf des Mikrocode-Befehls chkweak................... 60 4.1 Darstellung der Struktur des implementierten Systems............ 64 4.2 Darstellung der Internings von Objekten der Klasse String........ 68 4.3 Klassendiagramm zur Ressourcenverwaltung mit schwachen Referenzen.. 68 4.4 Abhängigkeit der Dauer des Heapscan und der Weak-Phase......... 69

VIII Abbildungsverzeichnis 4.5 Darstellung des Verhaltens bei der Behandlung von SoftReferences.. 70 4.6 Vergleich der Häufigkeit ausgeführter Speicheroperationen.......... 71 4.7 Verteilung der durch die Schreib-Barriere verursachten Verzögerungen... 72

Tabellenverzeichnis 2.1 Unterstützung schwacher Referenzen in verschiedenen Java-Standards.... 9 3.1 Auflistung der zunächst vom GC-Bus unterstützten Befehle......... 43 3.2 Unterstützte Klassen des Pakets java.lang.ref für erweiterte Phasen des Objektlebenszyklus............................ 53 4.1 Vergleich der benötigten Hardware-Ressourcen zwischen ZPU und Open- FIRE, wenn diese in einer Minimalumgebung verwendet werden....... 63 4.2 Vergleich der benötigten Hardwareressourcen unter Verwendung der alten bzw. neuen Speicherverwaltung........................ 65 4.3 Anteil der Faults an den durchgeführten Operationen zum Überschreiben einer Referenz................................. 71 4.4 Der durch die Schreib-Barriere verursachte Overhead bei den Testprogrammen FScript und SkyRoads........................... 72

1 Einleitung Automatische Speicherverwaltung ist eines der wichtigsten Konzepte moderner, objektorientierter Programmiersprachen wie C# oder Java und führt durch die Entlastung des Entwicklers zu einer deutlichen Verkürzung der Entwicklungszeit sowie zu einer Steigerung der Qualität des Softwaresystems. Besonders das umfangreiche Sicherheitskonzept, die Existenz zahlreicher Bibliotheken sowie die Möglichkeit, ein einmal geschriebenes Programm auf jedem System ausführen zu können, auf welchem die Java Virtual Machine (JVM) verfügbar ist, macht Java als Entwicklungsumgebung auch für eingebettete Systeme zunehmend interessanter. Gerade im eingebetteten Bereich werden an ein System häufig Forderungen bezüglich der maximalen Antwortzeit sowie des Datendurchsatzes gestellt, deren Einhaltung die Entwickler der JVM vor besondere Herausforderungen stellt. Insbesondere die Umsetzung der Garbage Collection erweist sich als sehr kompliziert im Zusammenhang mit Echtzeitanforderungen. Während in C++ die Speicherfreigabe manuell veranlasst wird und mit den dabei aufgerufenen Destruktoren die Implementierung von Abschlussaktionen vor der Freigabe eines Objektes leicht möglich ist, gestaltet sich dies in Java durch den Garbage Collector wesentlich komplizierter. So wird der zunächst einfache Objektlebenszyklus durch zusätzliche Zustände erweitert, mit deren Hilfe eine Benachrichtigung über die Freigabe eines Objektes möglich wird. Obwohl sog. Finalizer-Methoden automatisch durch den Garbage Collector vor der Freigabe der Objekte aufgerufen und zur Implementierung von Abschlussaktionen genutzt werden können, birgt deren Verwendung zahlreiche Risiken und Probleme. Ein anderer Ansatz wird durch sog. schwache Referenzen verfolgt: Eine schwache Referenz hält ein Objekt nicht am Leben, d.h. ein nur noch schwach referenziertes Objekt kann vom Garbage Collector freigegeben werden. Das Programm wird über diese Freigabe mit Hilfe des die schwache Referenz repäsentierenden Objektes, informiert. Dadurch können ebenfalls Abschlussaktionen realisiert werden, ohne dabei jedoch den durch die Verwendung von Finalizern verursachten Problemen zu begegnen. Während die meisten JVMs im Desktop-Bereich sowohl schwache Referenzen als auch Finalizer unterstützen, werden diese nützlichen Funktionen bei eingebetteten Systemen meist vernachlässigt. Das Ziel dieser Arbeit besteht im Entwurf und der Implementierung einer erweiterten Speicherverwaltung für den Java-Bytecode-Prozessor SHAP mit der Unterstützung erweiterter Phasen des Objektlebenszyklus. Aufbauend auf dem bisher vorhandenen, vollständig in Hardware implementierten Garbage Collector, sollen auch weiterhin Echtzeitkriterien erfüllt und Vorhersagen bezüglich der Antwortzeit einzelner Operationen gegeben werden können. Dabei soll auch die weitere Entwicklung der SHAP-Plattform hinsichtlich der Unterstützung mehrerer CPUs berücksichtigt sowie Einschränkungen des bisherigen Collectors behoben werden.

2 Stand der Technik 2.1 Garbage Collection 2.1.1 Allgemein Der Begriff Garbage Collection bezeichnet im Bereich der Speicherverwaltung die automatische Freigabe nicht länger benötigter Speicherbereiche. Da alle Objekte, welche vom Programm nicht mehr referenziert werden von diesem auch nicht benötigt werden können, wird generell von der Vereinfachung Gebrauch gemacht, lediglich nicht referenzierte Objekte zu suchen und diese schließlich freizugeben. Es existieren zwei verschiedene Herangehensweisen zur Realisierung eines Garbage Collectors (GCs): Die Zählung der Referenzen auf ein Objekt (Reference Counting) und die Traversierung des Objektgraphen (Tracing) nach erreichbaren Objekten. Sehr umfangreiche Informationen über Garbage Collection können [Jone96] und [Wils94] entnommen werden, worauf sich auch die folgenden Absätze beziehen. 2.1.2 Zählung der Referenzen Der Zählung der Referenzen liegt die Idee zugrunde, jedes Objekt mit einem Zähler zu versehen, welcher die aktuelle Anzahl der Referenzierungen auf das Objekt widerspiegelt. Wird eine Referenz hinzugefügt, so wird der Zähler inkrementiert, beim Löschen einer Referenz hingegen dekrementiert. Erreicht der Zähler dabei den Wert Null, so existieren keine weiteren Referenzen und das Objekt kann freigegeben werden. Die Freigabe erfolgt, sobald die letzte Referenz auf ein Objekt gelöscht wurde, wodurch das Verfahren automatisch inkrementell arbeitet. Es scheitert jedoch, sobald zyklische Strukturen auftreten, da dann die Referenzenzähler der Objekte innerhalb eines Zyklus auch ohne äußere Referenz nie den Wert Null erreichen. 2.1.3 Traversieren des Objektgraphen Verfahren auf Basis der Traversierung des Objektgraphen arbeiten prinzipiell zyklenorientiert. Mit Beginn eines GC-Zyklus wird ausgehend vom sog. Root Set (bestehend aus Registern, globalen Variablen, Stacks, usw.), die transitive Hülle über alle erreichbaren Objekte gebildet, wobei all die Objekte freigegeben werden können, die während des Scans nicht gefunden werden konnten. Durch das zyklenorientierte Vorgehen sammelt sich zunächst eine gewisse Menge nicht erreichbarer Objekte an, welche schließlich am Stück freigegeben werden. Algorithmen, welche die Applikation während der Garbage Collection anhalten, werden als Stop The World bezeichnet. Läuft die Applikation hingegen während der Garbage

4 2 Stand der Technik Collection weiter, so wird zwischen incremental und concurrent Garbage Collection unterschieden. Die Applikation wird in diesem Zusammenhang als Mutator bezeichnet. Ein incremental Collector teilt den GC-Zyklus in zahlreiche kleine Abschnitte ein, für welche die Applikation unterbrochen wird, ein concurrent Collector arbeitet hingegen parallel zum Mutator. In beiden Fällen sind umfangreiche Maßnahmen zur Synchronisation erforderlich, da sich der Objektgraph während der GC verändert. Zur Synchronisation kommen vor allem Schreib- bzw. Lese-Barrieren zum Einsatz. Während eine Schreib-Barriere eine Handler Routine ausführt, wenn eine Referenz geschrieben wird, dienen Lese-Barrieren im Allgemeinen zum Ersetzen gelesener Daten. Nahezu alle modernen Verfahren zur Garbage Collection basieren auf einem Tracing- Algorithmus, da diese so unempfindlich gegenüber zyklischen Datenstrukturen sind. Es wurden mehrere verschiedene Varianten entwickelt: Mark-and-Sweep Der GC-Zyklus ist in die zwei Phasen Mark und Sweep unterteilt. Während der Markierungsphase werden alle erreichbaren Objekte markiert sowie nach ausgehenden Referenzen durchsucht und in der Sweep-Phase schließlich all die Objekte freigegeben, welche nicht erreicht werden konnten. Mark-and-Compact Aufteilung in die beiden Phasen Mark und Compact, wobei alle während der Markierungsphase erreichbaren Objekte in der Kompaktierungsphase dicht hintereinander an andere Positionen im Speicher verschoben werden. Copying Garbage Collection Anstatt zunächst zu scannen und schließlich nicht erreichte Objekte freizugeben, werden bereits während des Scans alle erreichbaren Objekte sofort hintereinander in einen anderen Speicherbereich verschoben, wodurch alle nicht erreichbaren Objekte zurückbleiben und automatisch freigegeben sind. Der von Baker [Bake78] entwickelte inkrementelle Algorithmus ist der bekannteste, der dieses Verfahren nutzt. Der Speicher wird in die beiden Bereiche to-space und from-space geteilt. Zu Beginn eines GC-Zyklus werden die Rollen beider Bereiche vertauscht und alle erreichbaren Objekte nach to-space verschoben. Die Applikation wird währenddessen nicht angehalten, weshalb mit Hilfe einer Lese-Barriere sichergestellt werden muss, dass evtl. gelesene Zeiger auf from-space durch die neue Position des Objektes in to-space ersetzt werden. Wurde das Objekt nicht bereits vorher verschoben, so wird durch die Lese-Barriere auch das Verschieben des Objektes ausgelöst. Die sog. Generational Garbage Collection dient der Verkürzung der Dauer der GC- Zyklen. Hier wird versucht, aufgrund der Weak Generational Hypothesis [Wils94], die Garbage Collection vor allem auf junge Objekte zu konzentrieren, da diese mit höherer Wahrscheinlichkeit Garbage werden. Während einer Minor Collection werden lediglich die jungen Objekte überprüft, während die Major Collection alle Objekte einschließt. Da somit

2.2 Objektlebenszyklus 5 allocation and initialization in use deallocation reachable unreachable Abbildung 2.1: Darstellung des einfachen Objektlebenszyklus. Der grau hinterlegte Bereich zeigt an, wann das Objekt in Java im Objektgraphen erreichbar ist. alte Objekte seltener gescannt werden, wird mit Hilfe des sog. Remembered Sets gespeichert, welche alten Objekte auf junge Objekte referenzieren. Diese müssen auch in einer Minor Collection gescannt werden. Das Remembered Set wird im Allgemeinen mit Hilfe einer Schreib-Barriere aktualisiert, sobald eine Referenz auf ein junges Objekt in einem alten Objekt installiert wird. Während einer Major Collection wird das Remembered Set neu aufgebaut. 2.2 Objektlebenszyklus 2.2.1 Allgemeiner Objektlebenszyklus Mit dem Anlegen eines neuen Objektes beginnt, unabhängig von der verwendeten Programmiersprache, der Objektlebenszyklus (Object Life Cycle), welcher in Abbildung 2.1 allgemein dargestellt ist. Während in C++ [Stro98] die Allokation von Objekten sowohl auf dem Heap als auch auf dem Stack möglich ist, können in Java [Arn + 00] Objekte lediglich auf dem Heap angelegt werden. In beiden Programmiersprachen werden neue Objekte auf dem Heap durch den new-operator erstellt. Dieser reserviert den für das Objekt notwendigen Speicher und führt die Initialisierung des Objektes durch, indem ein Konstruktor aufgerufen wird. Nach der Initialisierung wird das Objekt von der Anwendung verwendet, bis dieses letztendlich nicht länger benötigt wird und der belegte Speicher somit freigegeben werden kann. Mit der Freigabe des Speichers endet auch der Lebenszyklus des Objektes. Zur Freigabe des Speichers ist in C++ der Operator delete vorgesehen, welcher zunächst den Destruktor des Objektes aufruft, in welchem der Programmierer vom Objekt belegte Ressourcen freigeben kann. In Java erfolgt die Freigabe des Speichers hingegen automatisch durch den GC (siehe 2.1.1), welcher selbstständig erkennt, dass Objekte nicht länger benötigt werden. Ein Objekt gilt als nicht länger benötigt, sobald im Objektgraphen ausgehend von den Roots keine Referenz mehr auf das Objekt existiert, dieses also nicht länger erreichbar ist.

6 2 Stand der Technik allocation and initialization in use resurrectable (to be finalized) (in C#) deallocation in use reachable finalizer reachable unreachable Abbildung 2.2: Darstellung des erweiterten Objektlebenszyklus mit Finalizern. 2.2.2 Erweiterte Phasen des Objektlebenszyklus Finalizer Analog der in C++ vorhandenen Destruktoren bieten Sprachen mit automatischer Speicherverwaltung, wie Java oder C# [ECMA06], oft sogenannte Finalizer [Boeh03] an. Diese werden durch den Garbage Collector automatisch aufgerufen, bevor dieser das Objekt freigibt. So kann der Programmierer ebenfalls verschiedene Abschlussaktionen implementieren. Durch die Hinzunahme von Finalizern wird der Lebenszyklus eines Objektes erweitert (siehe Abb. 2.2). So werden Finalizer vor der Freigabe eines Objektes ausgeführt, wobei diese zunächst in eine Queue eingeordnet und durch separate Threads ausgeführt werden. Da auch auf andere, vom Objekt ausgehend referenzierte Objekte durch den Finalizer zugegriffen werden kann, darf der gesamte, vom zu finalisierenden Objekt ausgehende Teilgraph nicht vorzeitig freigegeben werden. Stattdessen sind die entsprechenden Objekte weiterhin zumindest finalizer-reachable. Da der Finalizer im Kontext des Objektes ausgeführt wird, zu welchem er gehört, ist prinzipiell eine Wiederbelebung (Resurrection) möglich, indem eine Referenz in einem anderen, noch erreichbaren Objekt installiert wird. Der GC muss also nach der Ausführung der Finalizer erneut überprüfen, ob die Objekte nicht länger erreichbar sind und somit freigegeben werden können. In Java werden Finalizer nur einmalig aufgerufen. Ein wiederbelebtes Objekt kann somit keinerlei Abschlussaktionen mit Hilfe von Finalizern mehr ausführen, wodurch auch die Wiederbelebung selbst nur maximal einmal möglich ist. Hingegen können Finalizer in C# [Rich00b] auch bei bereits wiederbelebten Objekten erneut zur Ausführung registriert werden, wodurch Objekte beliebig oft wiederbelebt werden können. Schwache Referenzen Eine andere Möglichkeit, über die Freigabe eines Objektes informiert zu werden, bieten sog. schwache Referenzen (Weak References). Diese sind Bestandteil zahlreicher Programmiersprachen mit Garbage Collection. Schwache Referenzen dienen der Interaktion mit

2.2 Objektlebenszyklus 7 Program Code Proxy object Referent Abbildung 2.3: Referenz- oder Proxy-Objekte zur Realisierung schwacher Referenzen. allocation and weakly in use initialization referenced deallocation strongly reachable weakly reachable unreachable Abbildung 2.4: Darstellung des erweiterten Objektlebenzyklus mit schwachen Referenzen. dem GC und führen unterschiedliche Stärken der Erreichbarkeit ein. Normale Referenzen werden daher auch als starke Referenzen (Strong References) bezeichnet. Schwache Referenzen werden meist durch sog. Proxy oder Referenz-Objekte realisiert: Zur Erzeugung einer schwachen Referenz wird ein Referenz-Objekt angelegt, welches die schwache Referenz auf das Zielobjekt realisiert und vom Programm aus normal, d.h. stark referenziert wird (siehe Abbildung 2.3). Das schwach referenzierte Objekt wird in diesem Zusammenhang als Referent bezeichnet. Eine schwache Referenz hindert den GC nicht daran, das Objekt freizugeben. Wird während der Garbage Collection festgestellt, dass ein Objekt ausschließlich schwach, d.h. nur entlang eines Pfades mit mindestens einem Referenz-Objekt erreichbar ist, so kann dieses freigegeben und das referenzierende Proxy-Objekt über diese Freigabe informiert werden, indem sein Referent auf null gesetzt wird. Der Programmierer kann wieder eine starke Referenz auf ein Objekt gewinnen, solang der Referent-Eintrag noch nicht auf null gesetzt wurde, andernfalls schlägt die Umwandlung fehl. Zusätzlich kann den Proxy-Objekten in den meisten Realisierungen eine Queue zugeordnet werden, in welche dieses bei Freigabe des Referents eingeordnet wird (Enqueuing). Sowohl mittels Enqueuing als auch mit der Wiedergewinnung einer starken Referenz kann das Programm über die Freigabe von Objekten durch den Garbage Collector informiert werden. Schwache Referenzen können daher verwendet werden, um verschiedene Abschlussaktionen zu implementieren, ohne dass dabei Finalizer zum Einsatz kommen müssen. Der Lebenszyklus der Objekte erweitert sich durch die Hinzunahme schwacher Referenzen um den zusätzlichen Zustand der schwachen Erreichbarkeit (siehe Abb. 2.4). Solange der GC das nur schwach referenzierte Objekt nicht gefunden und bearbeitet hat, kann dieser Zustand durch die Wiedergewinnung einer starken Referenz jederzeit verlassen werden. Andernfalls geht das Objekt durch das Löschen des Referent in den Zustand unreachable über und wird schließlich freigegeben.

8 2 Stand der Technik 2.3 Erweiterter Objektlebenszyklus in Java In Java werden sowohl Finalizer als auch verschiedene Typen schwacher Referenzen unterstützt [Arn + 00]. 2.3.1 Finalizer Verwendung Soll einer Klasse ein Finalizer hinzugefügt werden, so ist die Methode protected void finalize() zu überschreiben [Mid + 03]. Besitzt ein freizugebendes Objekt einen Finalizer, welcher noch nicht bereits aufgerufen wurde, so wird dieser vor der Freigabe des Objektes ausgeführt. Da durch den Finalizer eine Wiederbelebung möglich ist, kann das Objekt selbst sowie alle Objekte, die von diesem referenziert werden, frühestens im nächsten GC-Zyklus und nach Ausführung des Finalizers freigegeben werden (vgl. 2.2.2). Probleme Bei der Verwendung von Finalizern zur Realisierung von Abschlussaktionen treten zahlreiche Probleme auf, weshalb von deren Verwendung im Allgemeinen abgeraten wird [Bloc01]. Die wichtigsten dieser Probleme sind im Folgenden aufgelistet: 1. Die Finalizer der Super-Klassen werden nicht automatisch aufgerufen. Der Programmierer muss im Finalizer einer abgeleiteten Klasse somit selbst dafür sorgen, dass Abschlussaktionen der Basisklasse ausgeführt werden. 2. Während der Ausführung des Finalizers geworfene, jedoch nicht abgefangene Exceptions werden ignoriert. Das Programm erhält keinerlei Benachrichtigung. 3. Die Reihenfolge der Ausführung ist unbestimmt, wodurch die im Finalizer implementierten Abschlussaktionen auch auf bereits finalisierte Objekte zugreifen können. 4. Es ist nicht festgelegt, wann und in welchem Thread die Finalizer ausgeführt werden. Auch die gleichzeitige Ausführung mehrerer Finalizer in unterschiedlichen Threads ist möglich. 2.3.2 Schwache Referenzen Die für die Arbeit mit schwachen Referenzen bereitgestellten Klassen befinden sich im Paket java.lang.ref, welches seit Version 1.2 Bestandteil der Java-Spezifikation ist (vgl. Tab. 2.1). Es werden drei verschiedene Typen schwacher Referenzen bereitgestellt, welche jeweils eine unterschiedliche Stärke der Erreichbarkeit repräsentieren und von der gemeinsamen, abstrakten Basisklasse Reference abgeleitet sind:

2.3 Erweiterter Objektlebenszyklus in Java 9 Java Plattform Finalization schwache Referenzen J2SE + vollständig J2ME CDC + vollständig J2ME CLDC - teilweise seit CLDC 1.1, nur Reference und WeakReference unterstützt Tabelle 2.1: Unterstützung schwacher Referenzen in verschiedenen Java-Standards. Reference <<abstract>> * 0..1 ReferenceQueue SoftReference WeakReference PhantomReference Abbildung 2.5: Im Paket java.lang.ref bereitgestellte Klassen zur Arbeit mit schwachen Referenzen. SoftReference, WeakReference und PhantomReference (siehe dazu auch die Java-API Dokumentation [API06]). Das zugehörige Klassendiagramm ist in Abbildung 2.5 dargestellt. Den Konstruktoren der jeweiligen Reference-Typen wird eine Referenz auf das zu referenzierende Objekt den Referent übergeben. Mittels der Methode get() ist das Erzeugen einer starken Referenz auf den Referent möglich, solange dieser vom GC noch nicht freigegeben wurde. Die get()-methode der Klasse PhantomReference liefert hingegen stets den Wert null, weshalb die Wiedergewinnung einer starken Referenz nicht möglich ist. Durch einen Aufruf der Methode clear() wird der Referent gelöscht. Zur Benachrichtigung über die Freigabe kann an den Konstruktor außerdem ein Objekt von ReferenceQueue übergeben werden, in welches das Reference-Objekt ggf. eingeordnet wird. Das Enqueuing muss dabei nicht sofort nach dem Löschen des Referents erfolgen. Einer PhantomReference ist stets eine ReferenceQueue zuzuordnen. Eine schwache Referenz wird in Java prinzipiell wie in Abbildung 2.6 gezeigt erstellt. Das auf Zeile 1 erstellte Objekt ist zunächst über die Referenz obj stark erreichbar. In Zeile 3 wird nun ein Objekt von WeakReference erstellt und diesem sowohl die Referenz obj als Referent, als auch ein Objekt von ReferenceQueue übergeben. Da das Objekt weiterhin stark erreichbar ist, kann durch den GC in Zeile 6 keine Freigabe erfolgen und das Erzeugen einer (weiteren) starken Referenz in Zeile 7 ist möglich. Auch erfolgte keine Benachrichtigung mit Hilfe der ReferenceQueue. Wird hingegen die starke Referenz obj wie in Zeile 11 gelöscht, so kann durch den Garbage Collector festgestellt werden, dass das Objekt nur noch schwach erreichbar ist. Das Objekt wird freigegeben und das referenzierende Proxy-Objekt informiert. Das Erzeugen einer starken Referenz ist somit nicht länger möglich und der Aufruf von get() in Zeile 13 liefert null. Außerdem wurde

10 2 Stand der Technik O b j e c t o b j = new O b j e c t ( ) ; ReferenceQueue < Object > rq = new ReferenceQueue < Object > ( ) ; WeakReference < Object > wr = new WeakReference < Object >( obj, rq ) ; 5 / / run t h e GC System. gc ( ) ; System. o u t. p r i n t l n ( wr. g e t ( ) ) ; System. o u t. p r i n t l n ( rq. p o l l ( ) ) ; 10 / / now d e l e t e t h e s t r o n g r e f e r e n c e and run t h e GC o b j = n u l l ; System. gc ( ) ; System. o u t. p r i n t l n ( wr. g e t ( ) ) ; System. o u t. p r i n t l n ( rq. p o l l ( ) ) ; Abbildung 2.6: Erzeugung einer schwachen Referenz in Java. das Referenz-Objekt in die zugeordnete ReferenceQueue eingeordnet, weshalb dieses vom poll()-aufruf in Zeile 14 zurückgeliefert wird. Durch die drei unterschiedlichen Typen schwacher Referenzen sowie die Unterstützung von Finalizern existieren in Java sechs verschiedene Zustände der Erreichbarkeit, welche im Folgenden der Stärke nach absteigend sortiert aufgelistet sind [Venn99]: strongly-reachable: Ein Objekt ist strongly-reachable, wenn es im Objektgraphen über einen Pfad ausgehend von den Roots erreichbar ist, welcher ausschließlich aus starken Referenzen besteht. Ein so erreichbares Objekt wird vom GC nicht freigegeben. softly-reachable: Ein Objekt befindet sich im Zustand softly-reachable, wenn es nicht strongly-reachable ist und sich auf dem Pfad zum Objekt mindestens ein Objekt von SoftReference befindet. Solange im System genügend Speicher vorhanden ist, gleicht das Verhalten einer SoftReference dem gewöhnlicher, starker Referenzen. Das referenzierte Objekt wird daher nicht freigegeben. Ist hingegen der Speicher erschöpft, so werden SoftReferences genau wie WeakReferences behandelt. Es wird garantiert, dass die Referents aller SoftReferences gelöscht wurden, bevor von der JVM eine OutOfMemoryError-Exception geworfen wird. Hauptanwendungsgebiet der SoftReferences ist daher die Implementierung von Speicher-Caches, welche z.b. aufwändig zu rekonstruierende Daten aufnehmen. weakly-reachable: Ist ein Objekt weder strongly- noch softly-reachable und existiert auf dem Pfad zum Objekt im Objektgraphen mindestens ein Objekt von WeakReference, so ist das Objekt weakly-reachable.

2.4 SHAP 11 Stellt der GC fest, dass ein Objekt lediglich schwach erreichbar ist, so wird der Referent-Eintrag des zugehörigen Proxy-Objektes gelöscht und das Proxy-Objekt selbst ggf. in die zugeordnete ReferenceQueue eingeordnet. Besitzt das nur schwach erreichbare Objekt einen Finalizer, so wird dieses in den Zustand finalizer-reachable überführt. Wichtigstes Anwendungsgebiet ist die Implementierung von Canonicalizing Mappings, wobei von einem Objekt nur genau eine Instanz im Speicher existiert. Dieser wird ein eindeutiger Schlüssel zugeordnet, anhand dessen die Instanz bestimmbar ist. Ein Beispiel für eine derartige Zuordnung ist die Klasse WeakHashMap, in welcher die Schlüssel mit Hilfe von WeakReferences implementiert wurden. Wird ein Schlüssel nicht mehr referenziert, so wird der zugehörige Eintrag aus der WeakHashMap entfernt. resurrectable (finalizer-reachable): Ist ein Objekt weder strongly-, softly- noch weakly-reachable und besitzt das Objekt einen Finalizer, so wird dieses in den Zustand resurrectable oder finalizer-reachable gesetzt. Der Finalizer wird schließlich durch einen beliebigen Handler Thread ausgeführt. phantom-reachable: Ist ein Objekt ausschließlich durch mindestens ein Objekt von PhantomReference erreichbar, jedoch nicht strongly-, softly- oder weakly-reachable und ist dessen Finalizer bereits ausgeführt wurden, so befindet sich dieses im Zustand phantom-reachable. Im Gegensatz zu SoftReference und WeakReference wird der Referent durch den Garbage Collector nicht automatisch gelöscht. Die Benachrichtigung mit Hilfe der ReferenceQueue erfolgt daher sobald der Zustand phantom-reachable festgestellt und nicht wenn dieser verlassen wird. Für den Übergang in den Zustand unreachable ist ein expliziter Aufruf von clear() auf dem zugehörigen Objekt von PhantomReference notwendig. Phantom-Referenzen werden genutzt um Abschlussaktionen auszuführen, nach denen das Objekt keinesfalls mehr wiederbelebt werden darf. unreachable: Ein Objekt, welches über keinen Weg im Objektgraphen mehr erreichbar ist, befindet sich im Zustand unreachable und kann freigegeben werden. 2.4 SHAP Die SHAP-Plattform [Pre + 07], bestehend aus der SHAP-Mikroarchitektur und der dazugehörenden Implementierung der Java Virtual Machine (JVM), erlaubt die direkte Ausführung von Java-Bytecodes ohne die Hilfe eines Betriebssystems. Der Mikrocode-gesteuerte 32-Bit-Prozessor wurde vollständig in VHDL implementiert und bietet Unterstützung für die Ausführung mehrerer Threads (Multithreading). Für alle

12 2 Stand der Technik SHAP Microarchitecture configurable Wishbone Bus CPU ALU Stack UART Data 8 Code 32 Method Cache 32 32 Memory Manager Garbage Collector 32 DDR: 16 SDR: 32 Controller Graphics Unit Ethernet MAC DMA Ctrl Memory SRAM DDR RAM Abbildung 2.7: Übersicht über die SHAP-Mikroarchitektur (aus [Zab + 07]). auszuführenden Operationen können Obergrenzen hinsichtlich der Ausführungszeit gegeben werden, weshalb die Architektur prinzipiell auch für Echtzeitanwendungen geeignet ist. 2.4.1 Aufbau der Architektur Der Aufbau der SHAP-Mikroarchitektur dargestellt in Abbildung 2.7 verfolgt einen streng modularen Ansatz: CPU Die CPU, bestehend aus Core und Stack, bildet den Kern der Mikroarchitektur und besitzt, neben dem den Mikrocode enthaltenden ROM, einen kleinen lokalen Speicher, die sog. Mikrocode-Variablen. Der sehr performanzkritische Stack wurde mit Hilfe der auf FPGAs vorhandenen Speicherblöcke realisiert. Die Komponente stellt jedem Thread einen eigenen, dynamisch wachsenden Stack zur Verfügung und erlaubt damit sehr schnelle Kontextwechsel. Memory Manager (MMU) Durch den Speichermanager wird die gesamte Funktionalität des Java-Heaps bereitgestellt. Der objektbasierte Speicherzugriff erfolgt mit Hilfe von Indirektionszeigern (Handles) und ermöglicht das Anlegen neuer sowie den Zugriff auf bestehende Objekte. Die Freigabe nicht länger benötigter Objekte geschieht hingegen mit Hilfe des integrierten Garbage Collectors, welcher in 2.6.1 noch genauer betrachtet wird. Method Cache Im Methodencache wird der ausführbare Code einzelner Methoden abgelegt, wodurch längere Wartezeiten aufgrund von Speicherzugriffen während der Ausführung vermieden werden können.

2.4 SHAP 13 gc MMUCtrl CPU refman alloc_seg segman MMUStat Memory Controller IO Bus Abbildung 2.8: Übersicht über die Struktur der Memory Management Unit der SHAP- Mikroarchitektur (nach [Reic07]). Integrierter Speicher-Controller Mit Hilfe des integrierten Speichercontrollers wird eine einheitliche Schnittstelle zum Zugriff auf unterschiedliche Speichertypen bereitgestellt. Derzeit wird sowohl SRAMals auch DDR-SDRAM unterstützt. Wishbone Bus Sowohl intern als auch extern angekoppelte IO-Geräte kommunizieren mit der CPU über den Wishbone Bus. 2.4.2 Memory Management Unit Der Aufbau der Memory Management Unit (MMU) von SHAP ist in Abbildung 2.8 schematisch dargestellt. Die Funktion der einzelnen Komponenten ist im Folgenden kurz dargestellt: refman: Der Reference Manager kapselt wichtige Funktionen zur Behandlung von Handles (Referenzen) und den mit diesen verknüpften Objekten. Die physische Adresse der Objekte sowie weitere Informationen wie die Objetgröße, werden in sog. Referenz- Einträgen gespeichert. Jedes Objekt ist genau einem Referenz-Eintrag zugeordnet, welcher für die gesamte Lebensdauer des Objektes gleich bleibt. Ein Handle verweist stets auf einen Referenz-Eintrag. Der CPU wird das Anlegen neuer Objekte ermöglicht, was immer im Allokationssegment geschieht. Die dem GC bereitgestellten Funktionen betreffen sowohl das Scannen und Verschieben von Objekten, als auch das Abrufen von Informationen wie deren Größe sowie die Wiedernutzbarmachung der Referenz-Einträge von durch den GC freigegebenen Objekten. segman: Der Segment Manager übernimmt die Verwaltung der Segmente des segmentierten

14 2 Stand der Technik Heaps und stellt dem GC Operationen für deren Bearbeitung und Freigabe bereit. Jedem Segment sind eine Reihe von Informationen bezüglich der enthaltenen Objekte zugeordnet, welche ebenfalls abgerufen werden können. Außerdem ist die Bereitstellung des Allokationssegments Aufgabe des Segment Managers. alloc_segs: Bereitstellung eines Segments zur Allokation neuer Objekte. Die Allokation erfolgt fortlaufend, wodurch eine Suche nach freiem Speicher nicht notwendig ist. Das Allokationssegment wird durch den Segment Manager ausgetauscht, sobald der freie Platz nicht genügt, um ein Objekt maximaler Größe allokieren zu können. gc: Enthält alle Komponenten des Garbage Collectors, welcher im Abschnitt zu SHAP in 2.6.1 noch genauer betrachtet wird. Der GC nutzt die von Reference- und Segment Manager bereitgestellten Operationen zur Behandlung von Objekten und Segmenten. MMUCtrl: Bereitstellung von Statusinformationen wie der Anzahl freier Referenz-Einträge und setzen von Konfigurationsoptionen der Garbage Collection. Die Kommunikation erfolgt mit Hilfe des IO-Busses. MMUStat: Dient der Untersuchung von Abläufen innerhalb der MMU während der Entwicklung. Die Komponente erlaubt das Zählen von Ereignissen sowie die Addition von Werten beim Auftreten bestimmter Ereignisse. Die gewonnenen Informationen werden periodisch über eine separate USB-Schnittstelle nach außen transferiert und können am PC weiterverarbeitet werden. 2.4.3 Weiterentwicklung Derzeit wird die SHAP-Plattform hinsichtlich der Möglichkeit erweitert, mehrere Cores in einem System zu betreiben und damit unterschiedliche Threads parallel ausführen zu können. Zunächst sollen die verschiedenen Cores den verwalteten Heap gemeinsam nutzen, weshalb in [Zab + 08b] bereits ein Multi-Port Memory Manager beschrieben wird. Dieser bietet verschiedenen Komponenten die Möglichkeit, sowohl neue Objekte anzulegen, als auch mit bestehenden Objekten zu arbeiten. Dazu kann für jede Komponente ein eigener Port hinzugefügt werden. Neben der Bereitstellung von Mechanismen zur Synchronisation durch den Multi-Port- Memory-Manager muss auch der integrierte Garbage Collector für den Betrieb mehrerer Cores vorbereitet werden.

2.5 Implementierung schwacher Referenzen in bestehenden Systemen 15 2.5 Implementierung schwacher Referenzen in bestehenden Systemen 2.5.1 JamVM Die JamVM [JamV] ist eine Java Virtual Machine, deren ausführbares Binary lediglich wenige hundert Kilobyte groß ist. Im Gegensatz zur KVM von Sun Microsystems (siehe 2.5.2) wird die JVM Spezifikation Version 2 vollständig unterstützt, wodurch auch alle in Java vorhandenen erweiterten Phasen des Objektlebenszyklus Unterstützung finden. Der implementierte GC realisiert einen Mark-and-Sweep Algorithmus und unterbricht die Applikation für die gesamte Dauer der Garbage Collection. Alle Informationen über den Ablauf der Garbage Collection sowie die Behandlung der erweiterten Phasen des Objektlebenszyklus, sind dem Quellcode der JamVM entnommen. Neben dem Typ steht für jedes Objekt auch ein während der Garbage Collection benötigter Erreichbarkeitszustand zur Verfügung. Anhand des Objekttyps kann während der Markierungsphase ermittelt werden, ob es sich um ein gewöhnliches Objekt, oder um einen Untertyp von java.lang.ref.reference handelt. Der Erreichbarkeitszustand des Objektes kann einen der folgenden Werte annehmen, welche der Stärke nach sortiert sind: 00 unmarked 01 phantom-marked 10 finalizer-marked 11 hard-marked Der Zustand hard-marked entspricht dabei einem stark erreichbaren Objekt. In der has_finalizer_list befinden sich Referenzen auf alle Objekte, die einen noch nicht bereits ausgeführten Finalizer besitzen. Die Referenzen werden bei der Erstellung der Objekte automatisch eingefügt. Die Liste run_finalizer enthält hingegen Referenzen auf alle Objekte, deren Finalizer ausgeführt werden muss. Dies geschieht durch einen separaten Thread. Ein weiterer separater Thread, der Reference-Handler, übernimmt das Enqueuing von Reference-Objekten in die ihnen zugeordnete ReferenceQueue. Der GC ordnet diese dazu in eine Queue ein, welche vom ReferenceHandler abgearbeitet wird. Die Markierungsphase beginnt mit dem Rücksetzen des Erreichbarkeitszustandes aller Objekte auf den Wert unmarked. Daraufhin werden die Root-Objekte gesammelt und von diesen beginnend der Objektgraph durchsucht. Für die gesamte Markierungs-Phase wird einmalig festgelegt, wie SoftReferences zu behandeln sind. So werden diese entweder als normale starke Referenzen oder als WeakReferences behandelt. Beim Scan eines Objektes wird stets der Erreichbarkeitszustand des zu scannenden Objektes an die ausgehend referenzierten Child-Objekte übertragen, wenn der neue Zustand stärker als der bisherige ist. Die Child-Objekte müssen daraufhin (erneut) gescannt werden. Wird festgestellt, dass es sich bei einem zu scannenden Objekt um ein Objekt vom Typ PhantomReference handelt, so wird der Referent lediglich als phantom-marked

16 2 Stand der Technik markiert. Wird ein Objekt vom Typ WeakReference gescannt, so wird der Referent nicht verfolgt, das Child-Objekt behält also seinen Erreichbarkeitszustand bei. Nachdem der Heapscan abgeschlossen ist, besitzen alle Objekte eine der drei Markierungen unmarked, phantom-marked oder hard-marked. Durch einen Abgleich der has_finalizer_list mit den aktuellen Erreichbarkeitszuständen der Objekte, werden all die Objekte ausfindig gemacht, deren Finalizer aufzurufen ist. Alle Objekte, die keine hard-marked-markierung besitzen, werden aus der has_finalizer_list entfernt und in die run_finalizer Liste eingefügt. Daraufhin wird ein Heapscan ausgehend von den Einträgen in run_finalizer durchgeführt, wobei alle so erreichbaren Objekte gescannt und finalizer-marked markiert werden, deren aktuelle Markierung phantom-marked oder schwächer ist. In der sich nun anschließenden Sweep-Phase werden die Erreichbarkeitszustände aller vorhandenen Objekte überprüft, wobei nur Objekte mit unmarked-markierung freigegeben werden. Wird ein Objekt vom Typ WeakReference oder SoftReference gefunden, so wird geprüft, ob der Referent eine hard-marked-markierung besitzt. Ist dies nicht der Fall, so wird der Referent auf null gesetzt und das Objekt ggf. der vom Reference Handler Thread zu bearbeitenden Queue hinzugefügt. Wird hingegen ein Objekt vom Typ PhantomReference gefunden, so erfolgt das Hinzufügen zur Queue des Reference Handlers, wenn der Referent eine phantom-marked-markierung besitzt. Die Referenz auf die einem Reference-Objekt zugeordnete ReferenceQueue wird beim Enqueuing gelöscht, wodurch dies lediglich einmal geschieht. In Abbildung 2.9 ist die Funktionsweise des Garbage Collectors unter Beachtung der erweiterten Phasen des Objektlebenszyklus dargestellt. Zunächst sind in (a) lediglich die Objekte A, P und W stark erreichbar und damit hard-marked markiert, die Objekte C, D und G hingegen nur über die PhantomReference P phantom-marked. Der Referent der WeakReference W wird im Heapscan nicht verfolgt. B und F besitzen außerdem einen noch nicht bereits ausgeführten Finalizer. Zum Ende des Heapscans sind in (b) auch die Objekte B und D hard-marked markiert, wobei Objekt D erneut gescannt wurde. Da Objekt F nicht erreicht werden konnte, wird in (c) die Referenz aus has_finalizer entfernt und zu run_finalizer hinzugefügt, weshalb sowohl F als auch G gescannt und finalizermarked markiert werden. Objekt G wird zu diesem Zweck erneut gescannt. Der Referent der schwachen Referenz W kann gelöscht und sowohl für W als auch P kann das Enqueuing durchgeführt werden. Das Objekt E konnte nicht erreicht und kann somit freigegeben werden. 2.5.2 Sun KVM Die KVM von Sun Microsystems [SunM00] ist eine JVM für besonders ressourcenbeschränkte Geräte und Teil der J2ME-Plattform. Sie läuft auf 16- und 32-Bit-Prozessoren und kann bereits mit wenigen Kilobyte RAM eingesetzt werden. Die KVM ist vollständig in ANSI-C implementiert und lässt sich sehr leicht auf neue Plattformen portieren. Der Garbage Collector der KVM [SunM03] verwendet einen Mark-and-Sweep Algorithmus und unterbricht die Applikation für den gesamten GC-Zyklus. Alle Referenzen sind direkte Zeiger auf Objekte im Speicher, weshalb bei einer Kompaktierung eine Aktuali-

2.5 Implementierung schwacher Referenzen in bestehenden Systemen 17 (a) (b) (c) A P E W A P E W A P E W B C F B C F B C F D G D G D G has_finalizer run_finalizer to_enqueue B F B F B F W P unmarked phantom marked finalizer marked hard marked Abbildung 2.9: Darstellung der Garbage Collection in der JamVM mit Beachtung der erweiterten Phasen des Objektlebenszyklus. In (a) ist der Zustand während des Heapscans dargestellt, während (b) einen Ausschnitt unmittelbar nach Beendigung des Heapscans zeigt. (c) zeigt schließlich den Zustand zum Ende des GC-Zyklus. sierung sämtlicher Referenzen durchgeführt werden muss. Die Kompaktierung ist daher optional und muss ggf. bereits während der Compilierung aktiviert werden. Jedes Objekt besitzt einen Header, in welchem neben dem Typ auch ein Markierungs-Bit vorhanden ist. Die CLDC Spezifikation Version 1.1 [JSR] sieht lediglich die Klassen Reference und WeakReference vor, welche vom GC besonderer Behandlung bedürfen. Zur Kennzeichnung einer schwachen Referenz ist daher der spezielle Typ GCT_WEAKREFERENCE vorgesehen, welcher jeder Instanz einer beliebigen, von Reference abgeleiteten Klasse zugeordnet wird. Zur Verwaltung von Ressourcen können Abschlussaktionen registriert werden, welche bestimmten Objekten zugeordnet und bei deren Freigabe ausgeführt werden. Diese als native-finalizers bezeichneten C-Routinen können jedoch keine Wiederbelebung des ihnen zugeordneten Objektes durchführen. Für jede solche Routine wird ein Objekt vom Typ GCT_WEAKPOINTERLIST erstellt, in welches sowohl ein Zeiger auf den auszuführenden native-finalizer, als auch eine begrenzte Anzahl von assoziierten Objekten abgelegt wird. Die in einem solchen Objekt abgelegten Zeiger halten die referenzierten Objekte nicht am Leben, sie werden vom GC daher ignoriert. Der GC-Zyklus beginnt, nachdem eine Allokation nicht erfolgreich durchgeführt werden konnte, mit der Initialisierung der beiden Listen WeakPointers und WeakReferences, welche zunächst keine Einträge enthalten. Nach der Markierung der Root-Objekte wird der Heap durchlaufen und alle markierten Objekte hinsichtlich ausgehender Referenzen untersucht, wobei alle gefundenen Objekte ebenfalls markiert und gescannt werden. Für jedes zu scannende Objekt wird zunächst der Typ betrachtet. Wird dabei ein Objekt vom Typ