Learn · concepts

Cos'è JNI? Interfaccia Nativa Java per Modder

JNI (Java Native Interface) è il ponte integrato di Java per chiamare C/C++ dalla JVM e viceversa. È il metodo standard con cui un mod di Minecraft carica una libreria nativa.

TRtrolPublished 6 min read

Cos'è JNI in termini semplici?

JNI (Java Native Interface) è la parte di Java che collega due mondi: il bytecode gestito sulla JVM e il codice macchina compilato scritto in C o C++. Un metodo riceve la parola chiave native e non ha un corpo, e a runtime la JVM lo collega a una funzione corrispondente in una libreria nativa caricata. Da lì, entrambe le parti scambiano argomenti e valori di ritorno.

Il confine non è unidirezionale. La funzione nativa riceve un puntatore JNIEnv, e tale handle è la porta di accesso a Java: espone chiamate per la lettura e la scrittura di campi Java, l'invocazione di metodi Java, la costruzione di oggetti e la generazione di eccezioni. Quindi lo stesso ponte che permette a Java di chiamare verso l'esterno permette anche a C di chiamare verso l'interno.

Perché un mod o un client di Minecraft dovrebbe interagire con il codice nativo?

La maggior parte del codice Java non ha bisogno di JNI. Un mod lo utilizza quando la JVM da sola non può svolgere il compito, di solito per la grafica, l'audio, l'integrazione con il sistema operativo o una routine che deve essere veloce. La libreria nativa svolge il lavoro pesante e JNI è il modo standard per caricarla e richiamarla.

Riutilizzare una libreria C collaudata

Un codice base nativo e consolidato già risolve il problema. Riscriverlo in Java sarebbe uno sforzo inutile, quindi la mod si lega ad esso.

Raggiungi il sistema operativo

Alcune funzionalità della piattaforma non dispongono di un'API Java pura. Una chiamata nativa diventa l'unico percorso per accedervi.

Percorso infuocato

Una routine numerica stretta o di basso livello può essere eseguita più velocemente come codice nativo compilato rispetto al bytecode sulla JVM.

Gestire hardware e driver

Il rendering, l'audio e l'accesso ai dispositivi si trovano spesso dietro le librerie native che la JNI espone al lato Java.

Come funziona una chiamata JNI end-to-end?

Il flusso è identico su ogni piattaforma: si dichiara il metodo in Java, si compila una funzione C corrispondente in una libreria condivisa, la si carica a runtime, si lascia che la JVM le colleghi per nome, quindi si effettua la chiamata. I nomi delle funzioni seguono uno schema fisso in modo che la JVM possa risolverli, e un header generato specifica la firma esatta da implementare.

FaseCosa succede
DichiarazioneUn metodo Java viene scritto con la parola chiave native e lasciato vuoto sul lato Java.
BuildUna funzione C o C++ viene compilata in una libreria condivisa: .dll su Windows, .so su Linux, .dylib su macOS.
CaricamentoJava chiama System.loadLibrary per caricare la libreria durante l'esecuzione del programma.
CollegamentoLa JVM associa ogni metodo native alla sua funzione C tramite una firma "name-mangled".
ChiamataJava invoca il metodo; il controllo passa al codice nativo e ritorna con un risultato.

Quanto costa a te JNI?

JNI è potente e presenta spigoli vivi. Poiché il codice nativo viene eseguito al di fuori della rete di sicurezza della JVM, un errore lì fa crashare l'intero processo anziché generare un'eccezione gestibile. La memoria allocata dal lato nativo è invisibile al garbage collector, quindi le perdite di memoria si accumulano a meno che non vengano liberate manualmente. Ogni attraversamento comporta un overhead, il che rende costose le chiamate oltre il confine all'interno di un ciclo stretto. E le build native sono bloccate alla piattaforma: una .dll di Windows non si carica su Linux senza una compilazione separata.

Cosa compra

  • Accesso diretto a librerie C e C++ esistenti
  • Un percorso verso le funzionalità del sistema operativo e dell'hardware che la JVM non può raggiungere
  • Esecuzione a velocità nativa per le routine più utilizzate

Costo

  • Un crash nativo può mandare in crash l'intera JVM
  • Gestione manuale della memoria senza un sistema di garbage collection di supporto
  • Build specifiche per piattaforma per la manutenzione e la distribuzione
  • Overhead che attraversa i confini nei loop critici

JNI rispetto alla Foreign Function and Memory API

JNI è il meccanismo originale, ma non è più l'unico. Le versioni più recenti di Java includono la Foreign Function and Memory (FFM) API, progettata per chiamare codice nativo con meno codice ripetitivo e garanzie di sicurezza più rigorose. JNI continua a dominare gli strumenti esistenti perché è presente ovunque e ben compreso.

JNIFFM API
EtàOriginale, presente fin dalle prime versioni di JavaModerna, aggiunta nelle ultime release
Codice ripetitivoHeader generati, funzioni C con nomi modificatiHandle dei metodi definiti in Java, nessuna funzione C di supporto
Sicurezza della memoriaManuale; errori possono corrompere la JVMSegmenti delimitati con controlli più severi
AdozioneDiffusa tra le librerie esistentiIn crescita, per i nuovi codebase

DOMANDE FREQUENTI

Prendi Terminus

Built by people who actually read the JVM internals.