Learn · concepts
Was ist ein Java-Klassenlader?
Ein Classloader findet und lädt Java-Klassen zur Laufzeit in die JVM. Hier erfahren Sie, wie sie funktionieren, warum die Hierarchie wichtig ist und warum Mods davon abhängig sind.
Ein Classloader ist der Teil der Java Runtime Environment, der eine Klassendatei findet und sie in die JVM lädt. Jedes Mal, wenn Ihr Code einen Typ zum ersten Mal verwendet, fragt die JVM einen Classloader: "Finde diese Klasse für mich." Der Loader lokalisiert den Bytecode, liest ihn, und die JVM definiert die Klasse im Speicher. Dieser Mechanismus ist in normalen Java-Programmen unsichtbar, stellt aber den genauen Ankerpunkt dar, den Mod-Frameworks nutzen, um Minecraft zu patchen.
Was ein Classloader tatsächlich tut
Ein Classloader wandelt einen Klassennamen in eine nutzbare Klasse innerhalb der laufenden JVM um. Sie fordern einen Typ an, der Loader findet die passende .class-Datei (oder generiert sie), verifiziert und verknüpft sie, und die Klasse ist bereit zur Verwendung.
Die Arbeit erfolgt in drei Schritten:
- Finden — Der Loader lokalisiert den Bytecode für die benannte Klasse (aus einer JAR-Datei, von der Festplatte, dem Netzwerk oder generiert zur Laufzeit).
- Verifizieren und Verknüpfen — Die JVM prüft, ob der Bytecode gültig ist, und löst Referenzen auf andere Klassen auf.
- Verwenden — Die Klasse ist bereit und Ihr Programm kann sie instanziieren.
Deshalb können Java-Programme Code dynamisch zur Laufzeit laden, der zur Kompilierzeit nie erwähnt wurde. Der Klassenname genügt dem Loader, um ihn zu finden.
Die Classloader-Hierarchie
Classloader sind in einer Eltern-Kind-Kette angeordnet, und die meisten von ihnen fragen zuerst ihren Eltern. Eine Standard-Java-Anwendung hat drei integrierte Classloader:
| Loader | Loads | Zweck |
|---|---|---|
| Bootstrap | Kern-Java-Runtime-Klassen | Stellt sicher, dass Kern-Typen wie java.lang.Object immer gleich sind |
| Plattform | Standardbibliotheksmodule außerhalb des Kerns | Java-Module und -Erweiterungen |
| Anwendung | Die eigenen Klassen Ihres Programms | Der Klassenpfad, den Ihr Programm angibt |
Wenn der Anwendungslader nach einer Klasse gefragt wird, delegiert er normalerweise zuerst nach oben. Er fragt den Plattformlader, der den Bootstrap-Lader fragt, der versucht, sie zu laden. Nur wenn kein Elternteil sie laden kann, versucht das Kind. Dies ist das Delegationsmodell und verhindert, dass dieselbe Klasse zweimal unter unterschiedlichen Namen geladen wird.
Klassenidentität und Funktionsweise
Eine Klasse wird sowohl durch ihren Namen als auch durch den Lader, der sie definiert hat, identifiziert. Zwei verschiedene Lader können jeweils eine Klasse namens com.example.MyClass laden, und die JVM behandelt sie als zwei völlig unterschiedliche Typen. Das mag obskur klingen, ist aber die Grundlage für alles Interessante, was Java kann.
Bootstrap Loader: java.lang.Object (the core Object)
Application Loader: java.lang.Object (if somehow re-loaded by app loader)
→ Two different types, same name
→ This breaks your program, which is why delegation prevents it
Benutzerdefinierte Klassenlader
Programme können eigene Klassenlader definieren. Ein benutzerdefinierter Lader kann Bytecode von überall lesen: aus einer JAR-Datei, dem Netzwerk, einer Datenbank oder aus Bytecode, das er zur Laufzeit generiert oder transformiert hat. Dies geschieht wie folgt:
- Plugin-Systeme laden Code dynamisch, ohne ihn zur Kompilierzeit zu kennen.
- App-Server (Tomcat, JBoss) führen mehrere Anwendungen in derselben JVM aus, jede mit ihrem eigenen Classloader.
- Minecraft-Mods patchen Spielklassen während des Ladens.
Warum das für Minecraft-Mods wichtig ist
Mods existieren aufgrund von benutzerdefiniertem Classloading. Ein Mod-Loader für Minecraft startet das Spiel über seinen eigenen benutzerdefinierten Classloader, um Spielklassen beim Laden zu sehen und zu modifizieren. Dieser Abfangpunkt ist der Ort, an dem die Magie geschieht.
Wenn ein Loader zwischen der Festplatte und der JVM sitzt, kann er eine Klasse direkt vor der Definition durch die JVM verändern:
Disk: obfuscated Minecraft class
↓
Mod Loader (custom classloader)
↓ [bytecode transformation happens here]
↓
JVM: defines the patched class
↓
Memory: game runs with mods active
Ein Mod kann eine Spielmethode umschreiben, neues Verhalten injizieren, Felder hinzufügen, ohne die Spiel-JAR-Datei auf der Festplatte zu berühren. Die Klasse, die die JVM letztendlich ausführt, ist die, die vom Mod-Framework modifiziert wurde. Dies ist genau so, wie Fabric, Forge und andere Mod-Loader Minecraft patchen.
Lazy Loading
Klassen werden nicht alle beim Start geladen. Die JVM lädt jede Klasse beim ersten tatsächlichen Gebrauch. Dies wird als Lazy Loading bezeichnet:
Program starts → JVM doesn't load String class yet
User calls System.out.println() → String is needed → Loader loads it
Das bedeutet, dass ein Classloader nur Klassen finden und laden muss, die Ihr Code tatsächlich berührt. Unbenutzte Klassen gelangen nie in den Speicher. Deshalb kann der Minecraft-Start eine Weile dauern (Mods fügen viele Klassen hinzu), aber nicht für immer einfrieren.
FAQ
Beim ersten Aufruf, nicht beim Start. Das erste Mal, wenn Ihr Code einen Typ berührt, fragt die JVM einen Classloader, ihn zu finden und zu definieren. Dies wird als Lazy Loading bezeichnet, und das ist der Grund, warum ungenutzter Code nie in den Speicher gelangt.
Ein Klassenlader fragt seinen Elternklassenlader, bevor er selbst versucht, eine Klasse zu laden. Die Elternkette führt bis zum Bootstrap-Lader. Dies verhindert, dass dieselbe Klasse unter demselben Namen von verschiedenen Ladeprogrammen zweimal geladen wird.
Ja, wenn unterschiedliche ClassLoader sie definieren. Die JVM identifiziert eine Klasse sowohl anhand ihres Namens als auch ihres definierenden Loaders, sodass Klassen mit gleichem Namen aus separaten Loadern als unterschiedliche Typen behandelt werden.
Um Klassen beim Laden abzufangen und sie im Speicher zu transformieren. Ein benutzerdefinierter Loader sitzt zwischen der Festplatte und der JVM und modifiziert Bytecode, bevor die JVM die Klasse definiert. So patchen Mods Minecraft, ohne die Spiel-JAR-Datei zu verändern.