[ Seminar "Java und Werkzeuge für das Web" ] ... [ Inhaltsverzeichnis ] ... [ zurück ] ... [ weiter ] ... [ Literaturverzeichnis ]
Übersicht: Speicher
Suns VM-Spezifikation sieht fünf verschiedene Speicherbereiche (Runtime Data Areas) vor, die vom Classloader und der Execution Engine verwendet werden und unterschiedliche Arten von Daten speichern.
Die Method Area enthält sämtliche statischen Informationen über Klassen. Dieser Speicherbereich wird vom Classloader gefüllt, sobald eine Klasse geladen wird.
Die Informationen, die in der Method Area gespeichert werden, sind im Einzelnen für jeden Typ[1]:
- Der voll qualifizierte Name (also inklusive Package-Namen, wobei in der VM Punkte durch Schrägstriche ersetzt sind, z.B.
java/lang/String
- Der voll qualifizierte Name des direkten Elterntyps, wenn vorhanden
- Seine Modifikatoren (
public, final, abstract
...)
- Sämtliche Schittstellen, von denen er erbt
- Sein Konstantenpool (eine Liste sämtlicher verwendeten Konstanten und symbolische Referenzen auf alle verwendeten Typen, Felder und Methoden). Mehr zum Konstantenpool siehe auch Kapitel 5.
- Name, Typ und Modifikatoren aller Felder
- Name, Rückgabetyp, Parameter und Modifikatoren aller deklarierten Methoden
- Bytecode, Informationen über Stackframe-Aufbau, und Exception-Tabelle für jede nicht-native, nicht-abstrakte Methode
- Alle statischen, nicht-konstanten Variablen der Klasse
- Referenz auf den verwendeten Classloader. Die wird benötigt, da Anforderungen, neue Klassen zu laden, an den Classloader delegiert werden, der die anfordernde Klasse geladen hat.
- Referenz auf die Instanz von
java.lang.Class
, die diesen Typen repräsentiert.
All diese Daten müssen auf irgendeine Art und Weise in der Method Area vorliegen. Die konkrete Form ist jedoch auch hier dem Programmierer der VM überlassen, ebenso wie die Größe der Method Area, die sowohl festgelegt als auch dynamisch sein kann. Bei der Method Area ist darauf zu achten, dass sie pro VM-Instanz nur ein einziges Mal existiert - das heißt, bei der Implementierung ist auf Threadsicherheit zu achten, da verschiedene Threads gleichzeitig darauf zugreifen können.
Während in der Method Area statische Daten gespeichert werden, dient der Heap zur Speicherung von dynamischen Informationen über konkrete Objekte. Für jedes Objekt sind dies sämtliche Instanzvariablen - sowohl die, die in seiner eigenen Klasse deklariert wurden, als auch die aus sämtlichen Vorfahren dieser Klasse.
Desweiteren beinhaltet der Heap für jedes Objekt eine Referenz auf den zugehörigen Eintrag in der Method Area. Auch der Heap existiert nur einmal pro Instanz der VM.
Der Java-Stack speichert lokale Variablen innerhalb von Methodenaufrufen, sowie Operanden für arithmetische Operationen. Bei jedem Methodenaufruf wird ein Stack-Frame auf den Stack gepusht, der Platz für die lokalen Variablen dieser Methode und einige Zusatzdaten bietet. Wird die Methode (durch return
oder durch eine Exception) verlassen, wird der zugehörige Frame wieder vom Stack genommen. Jeder Thread hat seinen eigenen Java-Stack.
Auf jedem Stackframe liegen folgende Informationen:
-
Lokale Variablen
Die lokalen Variablen werden auf dem Stackframe als nullbasiertes Feld aus words repräsentiert. Die Wortlänge der VM ist implementierungsabhängig und richtet sich nach den Gegebenheiten der Hardware, words müssen allerdings mindestens einen Platz von 32 Bit bieten. Dabei werden Variablen vom Typ long
oder double
immer als zwei Wörter gespeichert, brauchen also zwei "Slots" in diesem Speicherbereich.
Die ersten Einträge des Arrays sind immer die Methodenparameter, und zwar in der Reihenfolge, in der sie im Methodenaufruf deklariert sind. Bei nicht-statischen Methoden zählt das Objekt selbst als impliziter erster Parameter, befindet sich also immer an Position 0.
Die restlichen Einträge sind weitere in der Methode deklarierte lokale Variablen, die Reihenfolge ist nicht festgelegt.
-
Der Operanden-Stack
Jeder Stackframe enthält wiederum einen Stack, den so genannten Operandenstack, auf dem arithmetrische Operationen durchgeführt werden. Die Java-VM ist stackbasiert, das heißt, sie verwendet für Operationen keine Register, sondern nur diesen Stack. Eine Addition von zwei int
-Variablen von der Art i = i+j;
kann beispielsweise mit Hilfe folgender Bytecode-Sequenz durchgeführt werden (in Klammern jeweils das zugehörige Mnemonic des Java-Bytecodes [2]):
- Pushe lokale Variable 1 auf den Stack (
iload_1
)
- Pushe lokale Variable 2 auf den Stack (
iload_2
)
- Poppe die beiden oberen Elemente vom Stack, addiere sie und pushe das Ergebnis (
iadd
)
- Poppe den oberen Wert vom Stack und speichere ihn als lokale Variable 1 (
istore_1
)
-
Weitere Frame-Daten
Zusätzlich zu den oben erwähnten Informationen enthalten Stackframes noch mehrere Referenzen auf Einträge in der Method Area, die für die jeweilige Methode von Relevanz sind (siehe dazu auch Kapitel 5):
- Die von der Methode verwendete Exception-Tabelle
- Von der Methode verwendete Klassen
Zusätzlich können Stackframes beliebige weitere Informationen enthalten, die nicht Teil der Spezifikation sind aber von der konkreten Implementierung der VM verwendet werden (z.B. Debug-Informationen).
Der Program-Counter (PC) ist das einzige Register, welches die VM-Spezifikation vorsieht. Seine Funktion ist selbsterklärend: Im PC ist jeweils die Adresse des aktuell ausgeführten Befehls abgelegt. Es ist nicht festgelegt, ob diese Adresse als Offset relativ zum Methodenanfang oder als absolute Speicheradresse vorliegt - auch dies ist abhängig von der konkreten Implementierung.
Wird keine Java-, sondern eine native Methode ausgeführt, ist der Wert des PC undefiniert.
Natürlich gibt es auch vom PC ein Exemplar pro Thread.
Der Stack für native Methoden erfüllt die gleiche Aufgabe wie der Java-Stack, allerdings für native Funktionsaufrufe. Entsprechend ist sein Aussehen je nachdem, welche Bibliotheken man als Schnittstelle zum Betriebssystem verwendet - und auch je nach Betriebssystem - unterschiedlich.
[1] Unter 'Typ' werden im Folgenden Klassen und Interfaces verstanden.
[2] Der Java-Bytecode ist dahingehend optimiert, dass er für den Zugriff auf die lokalen Variablen mit niedrigem Index jeweils eigene Load- und Store- Instruktionen hat. Die Instruktion iload_1
benötigt also keine Parameter und ist nur ein Byte groß. Nur für Variablen mit hohem Index, die seltener verwendet werden, benötigt man zwei Byte (iload_n
mit dem Index als Parameter)
[ Seminar "Java und Werkzeuge für das Web" ] ... [ Inhaltsverzeichnis ] ... [ zurück ] ... [ oben ] ... [ weiter ] ... [ Literatur ]