Learn · concepts
Was ist JNI? Java Native Interface für Modder
JNI (Java Native Interface) ist die in Java integrierte Brücke, um C/C++-Code vom JVM aufzurufen und umgekehrt. Es ist die Standardmethode, mit der ein Minecraft-Mod eine native Bibliothek lädt.
Was ist JNI in einfachen Worten?
JNI (Java Native Interface) ist der Teil von Java, der zwei Welten miteinander verbindet: verwalteten Bytecode auf der JVM und kompilierten Maschinencode, der in C oder C++ geschrieben wurde. Eine Methode erhält das Schlüsselwort native und keinen Rumpf, und zur Laufzeit verknüpft die JVM sie mit einer übereinstimmenden Funktion in einer geladenen nativen Bibliothek. Von dort aus tauschen beide Seiten Argumente und Rückgabewerte aus.
Die Grenze ist nicht einseitig. Die native Funktion empfängt einen JNIEnv-Zeiger, und dieser Handle ist das Tor zurück in Java: er stellt Aufrufe zum Lesen und Schreiben von Java-Feldern, zum Aufrufen von Java-Methoden, zum Erstellen von Objekten und zum Auslösen von Ausnahmen bereit. Also lässt dieselbe Brücke, die Java das Ausrufen ermöglicht, auch C das Einrufen zu.
Warum würde ein Minecraft-Mod oder -Client nativen Code berühren?
Die meisten Java-Programme benötigen JNI nicht. Ein Mod greift darauf zurück, wenn die JVM allein die Aufgabe nicht bewältigen kann, meistens bei Grafiken, Audio, Betriebssystemintegration oder einer Routine, die schnell sein muss. Die native Bibliothek erledigt die schwere Arbeit, und JNI ist der Standardweg, um sie zu laden und aufzurufen.
Verwenden Sie eine bewährte C-Bibliothek erneut
Ein ausgereifter, nativ entwickelter Codebestand löst das Problem bereits. Eine Überarbeitung in Java wäre eine verschwendete Mühe, daher bindet sich die Modifikation stattdessen an ihn.
Das Betriebssystem erreichen
Einige Plattformfunktionen haben keine reine Java-API. Ein nativer Aufruf wird zur einzigen Möglichkeit, auf diese zuzugreifen.
Einen heißen Pfad beschleunigen
Eine enge numerische oder Low-Level-Routine kann als kompilierter, nativer Code schneller laufen als als Bytecode auf der JVM.
Treiber und Hardware
Rendering, Klang und Gerätezugriff finden sich häufig hinter nativen Bibliotheken, die JNI der Java-Seite zugänglich macht.
Wie funktioniert ein JNI-Aufruf von Anfang bis Ende?
Der Ablauf ist auf jeder Plattform identisch: Deklarieren Sie die Methode in Java, kompilieren Sie eine passende C-Funktion in eine Shared Library, laden Sie diese zur Laufzeit, lassen Sie die JVM sie anhand des Namens verknüpfen und rufen Sie dann über die Grenze hinweg auf. Funktionsnamen folgen einem festen Muster, sodass die JVM sie auflösen kann, und eine generierte Header-Datei legt die genaue Signatur für die Implementierung fest.
| Schritt | Was passiert |
|---|---|
| Deklarieren | Eine Java-Methode wird mit dem Schlüsselwort native geschrieben und auf der Java-Seite leer gelassen. |
| Erstellen | Eine C- oder C++-Funktion wird in eine Shared Library kompiliert: .dll unter Windows, .so unter Linux, .dylib unter macOS. |
| Laden | Java ruft System.loadLibrary auf, um die Bibliothek während der Programmausführung zu laden. |
| Verknüpfen | Die JVM ordnet jede native-Methode anhand einer Namens-Mangel-Signatur ihrer C-Funktion zu. |
| Aufrufen | Java ruft die Methode auf; die Kontrolle geht in nativen Code über und kehrt mit einem Ergebnis zurück. |
Was kostet Sie JNI?
JNI ist leistungsstark, hat aber scharfe Kanten. Da nativ erstellter Code außerhalb der Sicherheitsabschirmung der JVM ausgeführt wird, führt ein Fehler dort zum Absturz des gesamten Prozesses anstatt einer abgefangbaren Ausnahme. Speicher, den die native Seite allokiert, ist für den Garbage Collector unsichtbar, sodass Speicherlecks entstehen, es sei denn, sie werden manuell freigegeben. Jeder Übergang verursacht Overhead, was den Aufruf über die Grenze innerhalb einer engen Schleife teuer macht. Und native Builds sind plattformgebunden: eine Windows-.dll-Datei wird unter Linux ohne separate Kompilierung nicht geladen.
Was es kauft
- Direkter Zugriff auf bestehende C- und C++-Bibliotheken
- Ein Weg zu Betriebssystem- und Hardwarefunktionen, die die JVM nicht erreichen kann
- Nativer Geschwindigkeitsausführung für die wichtigsten Routinen
Was es kostet
- Ein nativen Absturz kann die gesamte JVM zum Absturz bringen.
- Manuelle Speicherverwaltung ohne Rückfallmöglichkeit durch Garbage Collector.
- Plattformspezifische Builds zur Wartung und Auslieferung.
- Overhead, der Plattformgrenzen überschreitet, in heißen Schleifen
JNI vs. die Foreign Function and Memory API
JNI ist der ursprüngliche Mechanismus, aber nicht mehr der einzige. Neuere Java-Versionen enthalten die Foreign Function and Memory (FFM) API, die entwickelt wurde, um nativen Code mit weniger Boilerplate und stärkeren Sicherheitsgarantien aufzurufen. JNI dominiert immer noch bestehende Toolchains, da es überall verfügbar und gründlich verstanden ist.
| JNI | FFM API | |
|---|---|---|
| Alter | Ursprünglich, seit den frühen Java-Versionen vorhanden | Modern, in neueren Versionen hinzugefügt |
| Boilerplate | Generierte Header, name-gemangelte C-Funktionen | In Java definierte Method Handles, kein zusätzliches C |
| Speichersicherheit | Manuell; Fehler können die JVM beschädigen | Begrenzte Segmente mit stärkeren Prüfungen |
| Akzeptanz | Allgegenwärtig in bestehenden Bibliotheken | Wachsend, in neueren Codebasen |
Häufig gestellte Fragen
Es ist integriert. JNI wird mit dem JDK und der JVM ausgeliefert, sodass die Java-Seite nichts Zusätzliches benötigt. Das Kompilieren des nativen Teils erfordert weiterhin eine C- oder C++-Toolchain für jede Zielplattform.
Nein. Der Java-Teil bleibt portabel, aber eine native Bibliothek wird für ein Betriebssystem und eine CPU kompiliert. Die plattformübergreifende Bereitstellung bedeutet, eine separate .dll, .so und .dylib-Datei zu erstellen und zu bündeln.
Nr. JNI ist der ursprüngliche Mechanismus. Jüngere Java-Versionen fügen die Foreign Function and Memory API hinzu, die sicherer sein und weniger Boilerplate-Code erfordern soll, aber JNI ist weiterhin weit verbreitet und gut verstanden.
Wenn die JVM etwas nicht direkt kann (mit einer GPU oder einem Audio-Backend kommunizieren, eine plattformspezifische API aufrufen oder eine leistungsentscheidende Routine ausführen), lädt ein Mod eine native Bibliothek und ruft über JNI in diese ein.
Erstellt von Leuten, die tatsächlich die JVM-Interna gelesen haben.