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.

TRtrolPublished 6 min read

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.

SchrittWas passiert
DeklarierenEine Java-Methode wird mit dem Schlüsselwort native geschrieben und auf der Java-Seite leer gelassen.
ErstellenEine C- oder C++-Funktion wird in eine Shared Library kompiliert: .dll unter Windows, .so unter Linux, .dylib unter macOS.
LadenJava ruft System.loadLibrary auf, um die Bibliothek während der Programmausführung zu laden.
VerknüpfenDie JVM ordnet jede native-Methode anhand einer Namens-Mangel-Signatur ihrer C-Funktion zu.
AufrufenJava 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.

JNIFFM API
AlterUrsprünglich, seit den frühen Java-Versionen vorhandenModern, in neueren Versionen hinzugefügt
BoilerplateGenerierte Header, name-gemangelte C-FunktionenIn Java definierte Method Handles, kein zusätzliches C
SpeichersicherheitManuell; Fehler können die JVM beschädigenBegrenzte Segmente mit stärkeren Prüfungen
AkzeptanzAllgegenwärtig in bestehenden BibliothekenWachsend, in neueren Codebasen

Häufig gestellte Fragen

Terminus erhalten

Erstellt von Leuten, die tatsächlich die JVM-Interna gelesen haben.