Die Architektur der Java VM

SeminarthemenDie Architektur der Java VM→ I. Virtuelle Maschinen • II.III.IV.V.VI.VII.

I. Virtuelle Maschinen

↑ oben

I. 1 Einordnung

Virtuelle Maschinen (kurz VM) sind keinesfalls ein besonders neue Technik. Bereits kurz nach dem Aufkommen der Hochsprachen gab es Ende der 60er Jahren mit dem O-Code (Objekt-Code) einen Zwischencode, der den eigentlichen Compiler maschinenunabhängig machte, und bereits interpretiert oder direkt weiter in maschinenabhängigen Code umgewandelt werden konnte. Allerdings war auch in den späten 70er Jahren die Rechenleistung der PC-Systeme meist noch zu gering, als dass die zusätzliche Indirektstufe durch einen Interpreter in Kauf genommen wurde. Später wurde der Zwischencode oder Bytecode für den Endnutzer unsichtbar bei interpretierten Sprachen, wie Perl, Tcl oder Prolog als ein Zwischenschritt des Maschinencodeerzeugungsvorgangs eingesetzt. Mit der steigenden Programmkomplexität suchte man vermehrt nach Entwicklungstechniken und einheitlichen Konzepten, um Programme effizienter und fehlerfreier entwickeln zu können. Neben den Entwurfsmustern des Software-Designs entwickelt man in diesem Zusammenhang auch das Konzept des Interpreters zur virtuellen Maschiene weiter. Inzwischen war auch die Rechenleistung von PC-Systemen derartig angesteiegen, dass die Indirektstufe einer VM kein Problem mehr darstelle. Im Gegenteil: Dies wird immer mehr zu einem Vorteil gegenüber fertig compiliertem Maschinencode. Da eine VM den Maschinencode bei jedem Programmlauf neu erzeugt, kann sie den Instruktionsatz des physikalischen Prozessors optimal nutzen, und wesentlich mehr Werte auf Konstanten (statt Variablen) abbilden, die für eine richtige Sprungvorhersage unproblematisch sind, was bei PC-Prozessoren einen beachtlichen Teil der Performance ausmacht. Dies ist ein Beispiel für die bei virtuellen Maschinen mögliche Laufzeitoptimierung, welche immer wieder mit neuen Konzepten weiterentwicklt werden kann, ohne dass bestehende Bytecode-Programme neu erzeugt werden müssten.

Das Schaubild in Abb. 1 zweigt nocheinmal den wesentlichen Unterschied zwischen der klassischen Maschienencodeerzeugung (in einem zusammenhängenden Gesammtprozess) und der Erzeugung des Bytecodes einer virtuellen Maschine.

klassisch Copilierungvirtualisierte Copilierung
Quellcode
Quellcode
   
Lexikalische Analyse
Lexikalische Analyse
   
Syntax-Analyse
Syntax-Analyse
   
Semantische Analyse
Semantische Analyse
   
Zwischensprachen Generator
Zwischensprachen Generator
   
Zwischencode
prinzipiel beliebiges Format
Bytecode
Format für VM spezifiziert
 
Code-Optimierer
 
Maschinencode-Generator
 
Maschinencode
evt. BS anh.
immer HW abh.
1: Compilierung und virtuelle Maschienen

Entscheidend ist der maschinen- und betriebssystemunabhängige Zwischencode einer virtuellen Maschine. Dieser als Bytecode bezeichnete Zwischencode ist immer genau spezifiziert, um auf allen zugehörigen VMs ablaufen zu können. Diese interpretieren diesen zur Laufzeit auf dem Zielsystem und wandeln ihn in äquivalenten nativen Maschienencode um. So können Programme in Form von Bytecode auf jeder Architektur ausgeführt werden, auf der ihre virtuelle Maschine implementiert wurde.

↑ oben

I. 2 Funktionsprinzip

Der Bytecode

Der wesentliche Unterscheid eines Bytecodes zu einem herkömmlichen Zwischencode liegt in der Zielsetzung und dem Aufbau seines Befehlssatzes. Für herkömmlichen Zwischencode war vorallem entscheidend, dass...

Der Aufbau eines Bytecodes setzt zudem vorraus, dass...

Da beinahe alle heute eingesetzten Prozessoren als kleinste Einheit den Typ byte verwenden, nutzen die meisten virtuellen Maschienen diesen auch als grundlegende Einheit ihres Zwischencodes, was zu der Bezeichnung Bytecode führte.

Durch die zusätzlich kodierten Information eines Bytecodes kann dieser gewöhnlich auch wieder decompiliert werden. D.h. der Quellcode kann durch einen Decompiler zurückgewonnen werden. Allerdings entspricht der so erzeugte Quellcode dem Orginal nur funktionell. Dennoch ist die Ähnlichkeit zwischen Orginal und generiertem Quellcode inzwischen verblüffend groß. Dies kann natürlich auch genutzt werden, um den Bytecode in eine andere Programmiersprache zu übersetzen, für die ein entsprechender Decompiler existiert.

Die virtuelle Maschine

Die Bezeichnung virtuelle Maschine entstammt dem, einem tatsächlichen Prozessor ähnlichen, generellen Aufbau eines VM-Interpreters. Aber auch Komponenten klassicher Interpreter sind enthalten. Von diesen unterscheidet sich eine VM vorallem durch

Bytecode
Anwendung 1
Bytecode
Anwendung n
   
Virtuelle Maschine (Interpreter)
Code- Optimierer Speicher- Organisation Thread- Organisation IO- Organisation
Stack/Register-"Maschine"
   
Betriebssystem 1
Betriebssystem n
   
Hardware A
Hardware X
2: Komponenten und Funktionsprinzip einer virtuellen Maschine

Der Bytecode wird zunächst vom Code-Optimierer allgemein oder Maschinenbezogen optimiert. Der so entstandene Code wird meist noch verifiziert, also auf Fehlerfreiheit und Sicherheit überprüft. Dies ist jedoch prinzipiel für die Funktion nicht entscheidend. Wird der Bytecode als gültig angesehen, wird er über die in Software nachgebildeten Register oder einen Stack ausgeführt. Die einzelnen Insruktionen beziehen sich dabei ausschließlich auf virtuelle Register oder den Stack. Diese können zwar tatsächlichen Registern der Maschine entsprechen, dies ist jedoch der Implementierung der virtuellen Maschine überlassen. Auch der von einem Programm benötigte Speicher wird von der virtuellen Maschine organisiert, welche diesen in den allermeisten Fällen natürlich auf realen Speicher abbildet. Ein großer Vorteil ist dabei die automatische Speicherverwaltung, die sich um das Freigeben nicht mehr benötigter Speicherbereiche kümmert. Wie dies ermittelt wird, ist ebenso der Implementierung der virtuellen Maschine überlassen. Auch die Organisation der Threads obliegt der VM. Sie kann diese auf Threads eines darunterliegenden Betriebssystems abbilden, oder selbst als einziger Thread auf BS-Ebene oder direkt auf der Zielhardware laufen, und die Organisation der virtuellen Threads selbst übernehmen.

↑ oben

I. 3 Virtualisierungsziele

Der Einsatz von Interpretern oder virtuellen Maschinen macht die Maschinencodeerzeugung im ersten Moment komplexer/aufwendiger als die bisher übliche direkte Generierung des Maschinencodes (vergl. Abb. 1). Dieser "Nachteil" kehrt sich jedoch mit der Zeit in einen Vorteil um, da der erhöhte Aufwand nur einmal aufgewendet werden muss, die VM dann aber dauerhaft eine Reihe von Vorteilen mitbringt, was auch das Ziele einer Virtualisierung ist. Auch die zunehmende Komplexität von Anwendungsprogrammen erfordert eine möglichst hohen Wiederverwendbarkeit von Software. Virtuelle Maschienen bieten hier eine abstarkte wiederverwendbare Ebene, die den Entwicklungaufwand von Anwendungssoftware verringert. Dies folgt ehr indirekt aus den Virtualisierungszielen:

Portabilität
Der Bytecode einer VM muss ohne Modifikationen auf allen Prozessorarchitekturen ausführbar sein, für welche eine virtuelle Maschine existiert. Daher sollte die VM selbst so aufgebaut sein, dass sie verbreiteten Architekturen zwar ähnelt, aber dennoch portabel ist, sodass ihre Instruktionen leicht auf viele Maschienensprachen übertragen werden können. Ein geschickt gewählter Bytecode kann erheblich dazu beitragen.
Flexibilität
Der Einsatz der VM sollte möglichst viele Metaprogrammierungstechniken (z.B. Reflektion oder Coderzeugung zur Laufzeit) ermöglichen, die dem Programmierer mehr Freiheit geben, und den Aufwand der Programmierung senken.
Effizienz
Die zur Verfügung stehenden Ressourcen der Zielplattform sollten von der VM möglichst so genutzt werden, dass Auf heutiger PC-Hardware ist auch das Bytecode-Format eine wichtiger Faktor für die Ausführungsgeschwindigkeit, da mit dessen Laufzeitoptimierung spürbare Geschwindigkeitsgewinne erzeihlt werden können. Entscheidend ist dabei, dass sich der Kontrollfluss anhand des Bytecode algorithmisch und/oder statistisch auswerten lässt.
Kompaktheit
Dies bezieht sich auf den Bytecode einer VM. Dieser sollte möglichst kompakt sein, da
Sicherheit
Die Verwendung einer spezifizierten Zwischenkodierung, dem Bytecode, erleichtert auch dessen Manipulation. Eine VM sollte daher Sicherungskonzepte enthalten, welche Der Bytecode ist, im Vergleich zu herkömlichen Maschienencode, jedoch niemals ein Nachteil von virtuellen Maschinen. Er ermöglicht lediglich die Bewertung und Kontrolle des ausgeführten Programms durch die VM, was diese auch nutzen sollte. Normaler Maschienencode kann heute so gut wie garnicht bewertet werden, und ist somit immer unsicherer und potentiell fähig, ein System in jeder erdenklichen Weise zu schädigen.
Interoperabilität
Die Interoperabilität kann als besonderer Gesichtspunkt der Flexibilität aufgefasst werden. Sie bezieht sich in diesem Zusammenhang auf die unterstützten Programmiersprachen. Interoperale virtuelle Maschienen ermöglichen es dem Programmierer möglichst viele Programmiersprachen nebeneinander zu verwenden, und Programme oder Programmfragmente verscheidener Sprachen miteinander zu verbinden. Grundsätzlich lassen sich so zwei Ebenen der Interoperabilität unterscheiden:
Pre-Bytecode
Möglichst viele Programmiersprachen können effizient in den Bytecode der VM übersetzt werden.
Post-Bytecode
Möglichst viele Programmfragmente anderer Zwischen- oder Maschienencodeformate können über den Bytecode der VM aufgerufen und so ausgeführt werden. Allerdings stellt diese Möglichkeit ein großes Problem für die Sicherheit dar, da der aufgerufene Code kaum verifiziert werden kann. Daher ist eine Unterstützung oft auch eine Grandwanderung zwischen Sicherheit und den Möglichkeiten der Interoperabilität.

Das letzte Ziel hat deutlich gemacht, dass die Ziele einer Virtualisierung auch miteinander konkurieren. Daher muss beim Entwurf einer virtuellen Maschine und des dazugehörigen Bytecodes immer auch ein an das Einsatzgebiet der VM angepasster Schwerpunkt festgelegt werden. Die Entwicklung der Datenverarbeitungssysteme hat hier deutlich gezeigt, dass die Sicherheit eines System und die damit verbundene Überprüfbarkeit der Programme ein besonders wichtigen Platz einnehmen sollten, auch wenn dies zu Lasten der übrigen Virtualisierungsziele geschieht.

→ weiter…

SeminarthemenDie Architektur der Java VM→ I. Virtuelle Maschinen • II.III.IV.V.VI.VII.