JVM-Architektur und wie funktioniert sie?
Jeder Java-Entwickler weiß, dass Bytecode von JRE ausgeführt wird, aber JRE ist eine Implementierung der JVM. „Diese analysiert den Bytecode, interpretiert den Code und führt ihn aus“.
Virtuelle Maschine ist eine Software-Implementierung der physischen Maschine.Java wurde mit dem Konzept von WORA (Write Once Run Anywhere) entwickelt.Der Compiler kompiliert die Java-Datei in eine Klassendatei, dann wird die Klassendatei in die JVM eingegeben, die sie lädt und ausführt.
Wie funktioniert die JVM?
- Class Loader Subsystem
- Runtime Data Area
- Execution Engine.
Nun, Lasst uns in die einzelnen Teile springen.
1.Class Loader SubSystem :
Das dynamische Laden von Klassen in Java wird vom Class Loader Subsystem durchgeführt. „Laden -> Verknüpfen -> Initialisieren der Klassendatei“ zum ersten Mal zur Laufzeit.
1.1. Laden : Klassen werden von dieser Komponente geladen.
- Bootstrap Class Loader – lädt die Klasse aus dem Bootstrap Klassenpfad (rt.jar -hohe Priorität)
- Extension Class Loader – lädt die Klasse, die in (jre/lib) enthalten ist.
- Application Class Loader – lädt die Klasse der Anwendungsebene.
1.2 Linking : Führt die Verifizierung, Vorbereitung und Auflösung für die geladene Klasse durch.
- Verify : Der Bytecode-Verifizierer prüft, ob der generierte Bytecode korrekt ist oder nicht, andernfalls wird ein Verifizierungsfehler (java.lang.VerifyError)
- Prepare : Für alle statischen Variablen im Bytecode wird Speicher allokiert und Standardwerte werden geladen.
- Resolve(Optional): Symbolische Referenzen der Klasse werden durch Originalreferenzen aus dem Methodenbereich ersetzt.
1.3 Initialisierung : Abschließende Phase des Klassenladers, hier werden alle statischen Variablen mit Originalwerten versehen.& Statischer Block wird ausgeführt.
2. Laufzeitdatenbereich : (JVM-Speicher) – unterteilt in 5 Komponenten
- Methodenbereich : hier werden alle Daten auf Klassenebene (Felder, Methode, statische Variable) gespeichert. Nur ein Methodenbereich pro JVM.
- Heap-Bereich: alle Objekte & entsprechende Instanz, Variable, Array werden gespeichert. Ein Heap pro JVM
- wobei der Heap &Methodenbereich nicht thread-sicher ist, sondern eine gemeinsam genutzte Ressource für die JVM
3. Stack-Bereich : Für jeden Thread wird zur Laufzeit ein neuer Stack angelegt. Für jeden Methodenaufruf wird ein Eintrag im Stack hinzugefügt, der Stack Frame genannt wird.
- Lokale Variablen werden im Stack-Speicher gespeichert
- Thread safe
- Stack Frame ist in drei Untereinheiten aufgeteilt:
3.1 Lokales Variablen-Array: bezogen auf die lokale Methode, Variablen werden eingefügt
3.2 Operanden-Stack: Zwischenoperation – dient als Laufzeit-Arbeitsbereich, um die Operation auszuführen
3.3 Frame-Daten: alle Symbole, die der Methode entsprechen, werden gespeichert, im Falle einer Ausnahme werden Catch-Block-Informationen aufbewahrt.
4. PC-Register: Jeder Thread verfügt über ein Register, in dem die aktuell ausgeführte Operation gespeichert wird (sobald eine Anweisung aktualisiert wird, wird das PC-Register auch die nächste Anweisung aktualisieren)
5. Native Method Stack: Hält native Methodeninformationen für jeden Thread.
3. Execution Engine: Bytecode, der im Laufzeitdatenbereich zugeordnet ist, wird ausgeführt.
- Interpreter: Interpretiert den Bytecode schneller, aber die Ausführung ist langsam.
- JIT Compiler : neutralisiert den Nachteil des Interpreters.Wenn er wiederholten Code findet, verwendet er den JIT Compiler.
Er kompiliert den Bytecode in nativen Code (nativer Code wird direkt für wiederholte Methodenaufrufe verwendet).
Zwischencode-Generator : Erzeugt Zwischencode, wenn erforderlich
Code-Optimierer: Optimiert den Code
Zielcode-Generator: Konvertiert in Maschinen- / Native-Code
Profiler: Hilft, die mehrfachen Methodenaufrufe zu finden (Auffinden von Hotspots)