Java-Bytecode - Java bytecode

Java-Bytecode ist der Befehlssatz der Java Virtual Machine (JVM).

Beziehung zu Java

Ein Java- Programmierer muss Java-Bytecode überhaupt nicht kennen oder verstehen. Wie jedoch im IBM developerWorks-Journal vorgeschlagen, " hilft es dem Java-Programmierer , Bytecode zu verstehen und zu wissen, welcher Bytecode wahrscheinlich von einem Java-Compiler generiert wird , so wie Assemblerkenntnisse dem C- oder C++- Programmierer helfen ."

Befehlssatzarchitektur

Die JVM ist sowohl eine Stapelmaschine als auch eine Registermaschine . Jeder Rahmen für einen Methodenaufruf hat einen "Operandenstapel" und ein Array von "lokalen Variablen". Der Operandenstapel wird für Operanden zu Berechnungen und zum Empfangen des Rückgabewerts einer aufgerufenen Methode verwendet, während lokale Variablen den gleichen Zweck wie Register erfüllen und auch zur Übergabe von Methodenargumenten verwendet werden. Die vom Compiler berechnete maximale Größe des Operandenstapels und des lokalen Variablenarrays ist Teil der Attribute jeder Methode. Jeder kann unabhängig von 0 bis 65535 Werten skaliert werden, wobei jeder Wert 32 Bit beträgt. longund doubleTypen, die 64 Bit haben, belegen zwei aufeinanderfolgende lokale Variablen (die nicht 64-Bit-ausgerichtet im lokalen Variablenarray sein müssen) oder einen Wert im Operandenstapel (werden jedoch als zwei Einheiten in der Tiefe des Stapels gezählt) .

Befehlssatz

Jeder Bytecode besteht aus einem Byte, das den Opcode darstellt , zusammen mit null oder mehr Bytes für Operanden.

Von den 256 möglichen Byte langen Opcodes sind ab 2015 202 in Gebrauch (~79%), 51 sind für die zukünftige Verwendung reserviert (~20%), und 3 Befehle (~1%) sind dauerhaft für JVM-Implementierungen reserviert, um verwenden. Zwei davon ( impdep1und impdep2) sollen Fallen für implementierungsspezifische Software bzw. Hardware bereitstellen. Die dritte wird für Debugger verwendet, um Breakpoints zu implementieren.

Anweisungen lassen sich in eine Reihe von großen Gruppen einteilen:

  • Laden und speichern (zB aload_0, istore)
  • Arithmetik und Logik (zB ladd, fcmpl)
  • Typumwandlung (z. B. i2b, d2i)
  • Objekterstellung und -manipulation ( new, putfield)
  • Operandenstapelverwaltung (zB swap, dup2)
  • Kontrollübertragung (zB ifeq, goto)
  • Methodenaufruf und -rückgabe (zB invokespecial, areturn)

Es gibt auch einige Anweisungen für eine Reihe spezialisierterer Aufgaben wie das Auslösen von Ausnahmen, die Synchronisierung usw.

Viele Anweisungen haben Präfixe und/oder Suffixe, die sich auf die Operandentypen beziehen, mit denen sie arbeiten. Diese sind wie folgt:

Präfix Suffix Operandentyp
i ganze Zahl
l lang
s kurz
b Byte
c Charakter
f schweben
d doppelt
a Hinweis

Zum Beispiel iaddwerden zwei ganze Zahlen addiert, während daddzwei Doubles hinzugefügt werden. Die Anweisungen const, load, und storekönnen auch ein Suffix der Form annehmen , wobei n eine Zahl von 0–3 für und ist . Das Maximum n für unterscheidet sich je nach Typ. _nloadstoreconst

Die constAnweisungen schieben einen Wert des angegebenen Typs auf den Stack. Zum Beispiel iconst_5wird ein Integer (32-Bit-Wert) mit dem Wert 5 auf den Stack gelegt, während dconst_1ein Double (64-Bit-Gleitkommawert) mit dem Wert 1 auf den Stack gelegt wird. Es gibt auch ein aconst_null, das eine nullReferenz schiebt . Das n für die loadand- storeAnweisungen gibt den Index im lokalen Variablenarray an, von dem geladen oder gespeichert werden soll. Die aload_0Anweisung schiebt das Objekt in der lokalen Variablen 0 auf den Stack (dies ist normalerweise das thisObjekt). istore_1speichert die Ganzzahl oben auf dem Stapel in der lokalen Variablen 1. Bei lokalen Variablen über 3 wird das Suffix weggelassen und Operanden müssen verwendet werden.

Beispiel

Betrachten Sie den folgenden Java-Code:

outer:
for (int i = 2; i < 1000; i++) {
    for (int j = 2; j < i; j++) {
        if (i % j == 0)
            continue outer;
    }
    System.out.println (i);
}

Ein Java-Compiler könnte den obigen Java-Code wie folgt in Bytecode übersetzen, vorausgesetzt, das Obige wurde in eine Methode eingefügt:

0:   iconst_2
1:   istore_1
2:   iload_1
3:   sipush  1000
6:   if_icmpge       44
9:   iconst_2
10:  istore_2
11:  iload_2
12:  iload_1
13:  if_icmpge       31
16:  iload_1
17:  iload_2
18:  irem
19:  ifne    25
22:  goto    38
25:  iinc    2, 1
28:  goto    11
31:  getstatic       #84; // Field java/lang/System.out:Ljava/io/PrintStream;
34:  iload_1
35:  invokevirtual   #85; // Method java/io/PrintStream.println:(I)V
38:  iinc    1, 1
41:  goto    2
44:  return

Generation

Die gängigste Sprache, die auf Java Virtual Machine abzielt, indem sie Java-Bytecode erzeugt, ist Java. Ursprünglich existierte nur ein Compiler, der javac- Compiler von Sun Microsystems , der Java-Quellcode in Java-Bytecode kompiliert ; Da jedoch jetzt alle Spezifikationen für Java-Bytecode verfügbar sind, haben andere Anbieter Compiler geliefert, die Java-Bytecode erzeugen. Beispiele für andere Compiler sind:

Einige Projekte stellen Java-Assembler bereit, um das manuelle Schreiben von Java-Bytecode zu ermöglichen. Assemblercode kann auch maschinell generiert werden, beispielsweise durch einen Compiler, der auf eine virtuelle Java-Maschine abzielt . Bemerkenswerte Java-Assembler umfassen:

  • Jasmin , nimmt Textbeschreibungen für Java-Klassen, die in einer einfachen Assembler-ähnlichen Syntax mit Java Virtual Machine-Befehlssätzen geschrieben sind, und generiert eine Java-Klassendatei
  • Jamaica, eine Makro- Assemblersprache für die Java Virtual Machine . Java-Syntax wird für die Klassen- oder Schnittstellendefinition verwendet. Methodenrumpfs werden mithilfe von Bytecode-Anweisungen angegeben.
  • Krakatau Bytecode Tools enthält derzeit drei Tools: einen Decompiler und Disassembler für Java-Klassendateien und einen Assembler zum Erstellen von Klassendateien.
  • Lilac, ein Assembler und Disassembler für die Java Virtual Machine .

Andere haben Compiler für verschiedene Programmiersprachen entwickelt, um auf die Java Virtual Machine abzuzielen, wie zum Beispiel:

Ausführung

Heutzutage sind mehrere virtuelle Java-Maschinen verfügbar, um Java-Bytecode auszuführen, sowohl kostenlose als auch kommerzielle Produkte. Wenn die Ausführung von Bytecode in einer virtuellen Maschine unerwünscht ist, kann ein Entwickler mit Tools wie dem GNU Compiler for Java (GCJ) auch Java-Quellcode oder Bytecode direkt in nativen Maschinencode kompilieren . Einige Prozessoren können Java-Bytecode nativ ausführen. Solche Prozessoren werden als Java-Prozessoren bezeichnet .

Unterstützung für dynamische Sprachen

Die Java Virtual Machine bietet eine gewisse Unterstützung für dynamisch typisierte Sprachen . Der größte Teil des vorhandenen JVM-Befehlssatzes ist statisch typisiert – in dem Sinne, dass die Signaturen von Methodenaufrufen zur Kompilierzeit typgeprüft werden , ohne einen Mechanismus, um diese Entscheidung auf die Laufzeit zu verschieben oder die Methodenzuteilung durch einen alternativen Ansatz zu wählen.

JSR 292 ( Supporting Dynamically Typed Languages ​​on the Java Platform ) fügte eine neue invokedynamicAnweisung auf JVM-Ebene hinzu, um Methodenaufrufe basierend auf dynamischer Typprüfung (anstelle der vorhandenen statisch typgeprüften invokevirtualAnweisung) zu ermöglichen. Die Da Vinci Machine ist ein Prototyp einer virtuellen Maschinenimplementierung, die JVM-Erweiterungen hostet, die auf die Unterstützung dynamischer Sprachen abzielen. Alle JVMs, die JSE 7 unterstützen, enthalten auch den invokedynamicOpcode.

Siehe auch

Verweise

Externe Links