Bytecode ist eine kompakte, plattformunabhängige Zwischensprache, die nicht direkt vom Prozessor, sondern von einer virtuellen Maschine (VM) ausgeführt wird. Bekannte Beispiele sind der Java-Bytecode in .class-Dateien, der Python-Bytecode in .pyc-Dateien, die .NET-Zwischensprache CIL und WebAssembly im Browser.
Wie Bytecode entsteht
Ein Compiler (z.B. javac) übersetzt den Quelltext über die übliche Pipeline: lexikalische Analyse, Parser, AST, Zwischendarstellung und Codegenerierung. Statt Maschinencode für eine konkrete CPU erzeugt er Bytecode für die Ziel-VM. Bei interpretierten Sprachen wie Python passiert das automatisch beim ersten Import: Der Quelltext wird in Bytecode übersetzt und .pyc-Dateien werden als Cache gespeichert.
Stackmaschinencode
Die meisten Bytecode-Instruktionen arbeiten auf einem Operanden-Stack. Eine Addition sieht dann etwa so aus: iconst_3 legt die 3 auf den Stack, iconst_4 die 4, iadd nimmt beide, addiert sie und legt das Ergebnis zurück. Dadurch ist der Code sehr kompakt und lässt sich leicht verifizieren — Java prüft jede geladene Klasse mit einem Bytecode-Verifier auf sichere Typen und Speicherzugriffe.
Ausführung: Interpreter und JIT
Die VM kann Bytecode auf zwei Arten ausführen:
- Interpretieren: Die VM liest jede Instruktion und führt sie direkt aus — einfach und ohne Startzeit, aber langsam.
- JIT-Kompilierung: Die VM übersetzt heiß genutzte Bytecode-Abschnitte zur Laufzeit in echten Maschinencode (Just-in-Time). Das machen die JVM (HotSpot), .NET (JIT) und moderne JavaScript-Engines. Python nutzt standardmäßig nur den Interpreter.
Damit ist Bytecode die Grundlage für interpretierte Sprachen und für Plattformunabhängigkeit: Eine .class-Datei läuft auf jeder JVM — der Vorteil Write once, run anywhere. Die Runtime ist die VM samt Laufzeitbibliotheken, die den Bytecode ausführt.
Abgrenzung
Bytecode ist kein Maschinencode: Die CPU kann ihn nicht direkt ausführen, erst die VM übersetzt ihn (beim JIT) oder interpretiert ihn. Er ist auch keine generische Zwischendarstellung im Compiler-Sinne, sondern eine konkrete, gespeicherte Form mit festem Instruktionssatz, die als Datei verteilt wird. Klassische kompilierte Sprachen wie C erzeugen dagegen Objektdateien, die ein Linker zu einer ausführbaren Datei zusammenführt.
Beispiele in der Praxis
- Java:
javacerzeugt.class-Dateien,javastartet die JVM. - Python:
python -m py_compile datei.pyerzeugt sichtbaren Bytecode; normalerweise liegen die.pyc-Dateien im__pycache__-Ordner. - .NET: C#-Compiler erzeugen DLLs mit CIL, die die Common Language Runtime (CLR) ausführt.
- WebAssembly:
.wasm-Module sind der Bytecode für die Browser-VM und laufen dort nahezu mit nativer Geschwindigkeit.