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. long
und double
Typen, 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 ( impdep1
und 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 iadd
werden zwei ganze Zahlen addiert, während dadd
zwei Doubles hinzugefügt werden. Die Anweisungen const
, load
, und store
kö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.
_n
load
store
const
Die const
Anweisungen schieben einen Wert des angegebenen Typs auf den Stack. Zum Beispiel iconst_5
wird ein Integer (32-Bit-Wert) mit dem Wert 5 auf den Stack gelegt, während dconst_1
ein Double (64-Bit-Gleitkommawert) mit dem Wert 1 auf den Stack gelegt wird. Es gibt auch ein aconst_null
, das eine null
Referenz schiebt . Das n für die load
and- store
Anweisungen gibt den Index im lokalen Variablenarray an, von dem geladen oder gespeichert werden soll. Die aload_0
Anweisung schiebt das Objekt in der lokalen Variablen 0 auf den Stack (dies ist normalerweise das this
Objekt). istore_1
speichert 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:
- Eclipse-Compiler für Java (EuGH)
- Jikes , kompiliert von Java nach Java-Bytecode (entwickelt von IBM , implementiert in C++ )
- Espresso, kompiliert von Java nach Java-Bytecode (nur Java 1.0)
- GNU Compiler for Java (GCJ), kompiliert von Java nach Java-Bytecode; es kann auch in nativen Maschinencode kompilieren und war bis Version 6 Teil der GNU Compiler Collection (GCC).
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:
- ColdFusion
- JRuby und Jython , zwei Skriptsprachen basierend auf Ruby und Python
- Apache Groovy , optional typisierte und dynamische Allzwecksprache, mit statischen Typisierungs- und statischen Kompilierungsfunktionen
- Scala , eine typsichere Allzweck-Programmiersprache, die objektorientierte und funktionale Programmierung unterstützt
- JGNAT und AppletMagic, kompilieren von der Sprache Ada in Java-Bytecode
- C zu Java Byte-Code-Compiler
- Clojure , eine funktionale, unveränderliche, universelle Programmiersprache der Lisp- Familie mit einem starken Schwerpunkt auf Parallelität
- Kawa , eine Implementierung der Programmiersprache Scheme , auch ein Dialekt von Lisp .
- MIDletPascal
- JavaFX Script- Code wird in Java-Bytecode kompiliert
- Kotlin , eine statisch typisierte Allzweck-Programmiersprache mit Typrückschluss
- Der Object Pascal- Quellcode wird mit dem Free Pascal 3.0+-Compiler in Java-Bytecode kompiliert.
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 invokedynamic
Anweisung auf JVM-Ebene hinzu, um Methodenaufrufe basierend auf dynamischer Typprüfung (anstelle der vorhandenen statisch typgeprüften invokevirtual
Anweisung) 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 invokedynamic
Opcode.
Siehe auch
- Auflistungen von Java-Bytecode-Anweisungen
- Java-Klassendatei
- Liste der JVM-Sprachen
- Java-Backporting-Tools
- C zu Java Virtual Machine Compiler
- JStik
- Common Intermediate Language (CIL), Microsofts Rivale zu Java-Bytecode
- ObjectWeb ASM
- Byte-Code-Engineering-Bibliothek
Verweise
Externe Links
- Java Virtual Machine-Spezifikation von Oracle
- Programmiersprachen für die Java Virtual Machine
- Bytecode Visualizer – Bytecode-Viewer und Debugger (kostenloses Eclipse-Plugin)
- AdaptJ StackTrace – Debugging auf Bytecode-Ebene mit voller Kontrolle über den Stack, die lokalen Variablen und den Ausführungsfluss
- Java Class Unpacker – Plugin für Total Commander, ermöglicht das Öffnen von Klassendateien als komprimierte Archive und das Anzeigen von Feldern und Methoden als Dateien. Der Bytecode kann mit F3 . als Text angezeigt werden