La Interfaz Nativa de Java (o Interfaz de Método Nativo) es una interfaz de función externa diseñada para entornos de programación que no son Java . La JNI permite que el código Java llame y sea llamado por código nativo (es decir, cualquier código que utilice la interfaz binaria deJNIEnv la aplicación C ), aplicaciones nativas (programas específicos de una plataforma de hardware y sistema operativo ) y bibliotecas escritas en otros lenguajes como C , C++ y ensamblador . [ 1 ] [ 2 ] [ 3 ]
Java 22 introduce la API de funciones y memoria externas , que puede considerarse la sucesora de la interfaz nativa de Java. [ 4 ] [ 5 ]
Objetivos
JNI permite que los métodos nativos faciliten a las aplicaciones la realización de llamadas fuera de la plataforma Java, por ejemplo, cuando la biblioteca de clases estándar de Java no admite funciones específicas de la plataforma o una biblioteca de programas determinada. También se utiliza para modificar una aplicación existente (escrita en otro lenguaje de programación) y hacerla accesible desde aplicaciones Java. Muchas clases de la biblioteca estándar dependen de JNI para proporcionar funcionalidades tanto al desarrollador como al usuario, como las API de entrada/salida de archivos y de audio. Solo las aplicaciones y los applets firmados pueden invocar JNI.
El marco JNI permite que el código nativo manipule y consuma objetos Java . Un método nativo (marcado con la palabra clave `@` nativeen Java) puede crear objetos Java, inspeccionarlos y utilizarlos para realizar sus tareas. Un método nativo también puede inspeccionar y utilizar objetos creados por el código de la aplicación Java.
Una aplicación que depende de JNI pierde la portabilidad entre plataformas que ofrece Java (una solución parcial consiste en escribir una implementación separada del código JNI para cada plataforma y hacer que Java detecte el sistema operativo y cargue el correcto en tiempo de ejecución).
El código nativo no solo puede interactuar con Java, sino que también puede dibujar sobre una base de Java Canvas, lo cual es posible con la interfaz nativa Java AWT , disponible desde J2SE 1.3.
JNI también permite el acceso directo al ensamblador , sin un puente C. [ 6 ] El acceso a las aplicaciones Java desde el ensamblador es posible de la misma manera. [ 7 ]
El JNI es el punto de entrada entre los lenguajes utilizados para iniciar el arranque de la JVM. [ 8 ] [ 9 ] [ 10 ] [ 11 ]
Tipos de mapeo
Los tipos Java son los tipos primitivos predeterminados disponibles en Java, mientras que las firmas de tipo de la JVM son identificadores utilizados por la JVM para identificar la coincidencia de tipos en Java, mientras que los tipos nativos son tipos definidos por el propio JNI para mapear a tipos nativos. Para los tipos primitivos, se proporcionan las siguientes definiciones de tipo : [ 12 ]
Además, la firma de tipo de JVM hace referencia a la clase especificada de forma única por ese nombre; por ejemplo, la firma ( en archivos de clase ) hace referencia a la clase . Anteponer a la firma denota una matriz de ese tipo; por ejemplo, hace referencia a , y hace referencia a , hasta un máximo de 255 dimensiones.Lfully-qualified-class;Ljava.lang.String;Ljava/lang/String;java.lang.String[[Iint[][[Iint[][]
Estos tipos son intercambiables. Se puede usar jintdonde normalmente se usa un int, y viceversa, sin necesidad de conversión de tipos . Sin embargo, la asignación entre cadenas y matrices de Java a cadenas y matrices nativas es diferente; por ejemplo, jstringy char*no son tipos intercambiables, ni tampoco jintArrayy int[].
JNI contiene además las siguientes declaraciones para tipos de referencia de Java en <jni.h>: [ 13 ]
#ifdef __cplusplus class _jobject {}; class _jclass : public _jobject {}; // ... typedef _jobject * jobject ; typedef _jclass * jclass ; // ... #else struct _jobject ; // ... typedef struct _jobject * jobject ; typedef jobject jclass ; // ... #endifAquí, todos los tipos de referencia de Java se definen como tipos vacíos opacos que actúan como identificadores de objetos Java. Estos tipos no deben ser desreferenciados. [ 14 ]
Además, los siguientes tipos opacos se utilizan como manejadores:
Tenga en cuenta, sin embargo, que jfieldIDy jmethodIDson simplemente manejadores opacos y no corresponden a java.lang.reflect.Field/ java.lang.reflect.Method.
Diseño
La comunicación entre el código nativo y el código Java se realiza mediante JNI, que actúa como una ABI .
// Archivo jni.hstruct JNINativeInterface_ ; struct JNIEnv_ ;#ifdef __cplusplus typedef JNIEnv_ JNIEnv ; #else typedef const struct JNINativeInterface_ * JNIEnv ; #endifJNIEnvSe utiliza para acceder a la estructura de funciones (matriz de punteros a funciones), y cada función se identifica mediante un puntero único. [ 15 ] Según el lenguaje de programación, C o C++, la implementación puede ser ligeramente diferente. [ 16 ] Por ejemplo, en C:
// Archivo jni.hstruct JNINativeInterface_ {jint ( JNICALL * GetVersion ) // Declaración JNICALL de un puntero para identificar la función específica; ( JNIEnv * env ); // El parámetro env es un puntero al puntero JNIEnv, el puntero a puntero;jclass ( JNICALL * DefineClass ) ( JNIEnv * env , const char * name );jclass ( JNICALL * FindClass ) ( JNIEnv * env , const char * name );// Otros punteros a funciones... }Implementación
En el marco JNI, las funciones nativas se implementan en archivos .c o .cpp separados (C++ proporciona una interfaz ligeramente más simple con JNI). Cuando la JVM invoca la función, le pasa un , un y cualquier argumento Java declarado por el método Java. Por ejemplo, lo siguiente se convierte en un :JNIEnv*jobjectjstringconst char*
externo "C" {JNIEXPORT void JNICALL Java_ClassName_MethodName ( JNIEnv * env , jobject obj , jstring javaString ) { const char * NativeString = env -> GetStringUTFChars ( javaString , 0 );// Haz algo con la cadena nativaenv -> ReleaseStringUTFChars ( javaString , nativeString ); }}El envpuntero es una estructura que contiene la interfaz con la JVM. Incluye todas las funciones necesarias para interactuar con la JVM y trabajar con objetos Java. Todo lo que se puede hacer con código Java se puede realizar mediante este puntero JNIEnv. El argumento objes una referencia al objeto Java dentro del cual se ha declarado este método nativo.
Los tipos de datos nativos se pueden mapear a/desde tipos de datos Java. Para tipos compuestos, como objetos, matrices y cadenas , el código nativo debe convertir explícitamente los datos llamando a métodos en el JNIEnv.
Se pasa un puntero de entorno JNI ( JNIEnv*) como argumento para cada función nativa asignada a un método Java, lo que permite la interacción con el entorno JNI dentro del método nativo. Este puntero de interfaz JNI se puede almacenar, pero solo es válido en el hilo actual. Otros hilos deben llamar primero AttachCurrentThread()para conectarse a la máquina virtual y obtener un puntero de interfaz JNI. Una vez conectado, un hilo nativo funciona como un hilo Java normal que se ejecuta dentro de un método nativo. El hilo nativo permanece conectado a la máquina virtual hasta que llama DetachCurrentThread()para desconectarse. [ 17 ]
El marco JNI no proporciona recolección de basura automática para los recursos de memoria que no pertenecen a la JVM y que son asignados por el código que se ejecuta en el lado nativo. Por lo tanto, el código nativo es responsable de liberar explícitamente todos los recursos adquiridos.
En las plataformas Linux y Solaris, si el código nativo se registra como manejador de señales, puede interceptar las señales destinadas a la JVM. Se puede utilizar una cadena de responsabilidad para permitir la interoperabilidad del código nativo con la JVM. En las plataformas Windows, se puede emplear el Manejo Estructurado de Excepciones (SEH)try para encapsular el código nativo en bloques SEH , catchcon el fin de capturar las interrupciones de software generadas por la máquina (CPU/FPU) (como desreferencias de punteros nulos y operaciones de división por cero) y gestionar estas situaciones antes de que la interrupción se propague de vuelta a la JVM (es decir, al código Java).
The encoding used for the NewStringUTF(), GetStringUTFLength(), GetStringUTFChars(), ReleaseStringUTFChars() and GetStringUTFRegion() functions is "modified UTF-8",[18] which is not valid UTF-8 for all inputs, but a different encoding. The null character (U+0000) and codepoints not on the Basic Multilingual Plane (greater than or equal to U+10000, i.e. those represented as surrogate pairs in UTF-16) are encoded differently in modified UTF-8. Many programs actually use these functions incorrectly and treat the UTF-8 strings returned or passed into the functions as standard UTF-8 strings instead of modified UTF-8 strings. Programs should use the NewString(), GetStringLength(), GetStringChars(), ReleaseStringChars(), GetStringRegion(), GetStringCritical() and ReleaseStringCritical() functions, which use UTF-16LE encoding on little-endian architectures and UTF-16BE on big-endian architectures, and then use a UTF-16 to UTF-8 conversion routine.
For example, the following demonstrates a native implementation of a Java method HelloJNI::sayHello():
#include<stdio.h>#include<jni.h>JNIEXPORTvoidJNICALLJava_HelloJNI_sayHello(JNIEnv*env,jobjectobj){printf("Hello from C!\n");}Then, in Java, the function is called like so:
publicclassHelloJNI{static{// "hello" refers to libhello.so on LinuxSystem.loadLibrary("hello");// System.load() is other way to load a library.}publicnativevoidsayHello();publicstaticvoidmain(String[]args){newHelloJNI().sayHello();}}Performance
JNI incurs considerable overhead and performance loss under certain circumstances:[19]
- Function calls to JNI methods are expensive, especially when repeatedly calling a method.
- Native methods are not inlined by the JVM, nor can the method be JIT compiled, as the method is already compiled.
- A Java array may be copied for access in native code, and later copied back. The cost is linear in the size of the array.
- Si se le pasa un objeto al método o si necesita realizar una llamada de retorno, es probable que el método nativo realice sus propias llamadas a la JVM. El acceso a campos, métodos y tipos de Java desde código nativo invoca la reflexión de Java . Las firmas se especifican en cadenas y se consultan desde la JVM. Esto es lento y propenso a errores.
- Las cadenas de Java son objetos, tienen longitud y están codificadas. Acceder o crear una cadena puede requerir unaCopiar.
API de funciones externas y memoria
Java 22 introdujo la API de función y memoria externa, ubicada en java.lang.foreignque puede considerarse una especie de sucesora de la interfaz nativa de Java. [ 20 ] Tiene una interfaz más simple y generalmente requiere menos código repetitivo que JNI, debido a que ya no requiere la inclusión de <jni.h>en la biblioteca externa para invocar, y la API de función y memoria externa abstrae nativecompletamente el uso de la palabra clave. Admite la administración de memoria basada en regiones . La clase java.lang.foreign.MemorySegmentmodela un segmento contiguo de memoria que puede estar dentro o fuera del montón de la JVM, y java.lang.foreign.MemorySegmentse asigna un usando un java.lang.foreign.Arenaque controla la vida útil de la región de memoria que respalda su segmento de memoria asignado.
Ofrece java.lang.foreign.Arenalos siguientes tipos de arena: [ 21 ]
paquete org.wikipedia.examples ;import java.lang.foreign.Arena ; import java.lang.foreign.MemorySegment ; import java.lang.foreign.ValueLayout ;public class ForeignMemoryExample { public static void main ( String [] args ) { try ( Arena arena = Arena . ofConfined ()) { MemorySegment segment = arena . allocate ( 5 * Double . BYTES ); for ( int i = 0 ; i < 5 ; ++ i ) { segment . setAtIndex ( ValueLayout . JAVA_DOUBLE , i , i * 1.1 ); }for ( int i = 0 ; i < 5 ; ++ i ) { double value = segment . getAtIndex ( ValueLayout . JAVA_DOUBLE , i ); System . out . printf ( "Valor en el índice %d: %d%n" , i , value ); } } } }En cierto sentido, la API de Java Foreign Function and Memory permite la asignación directa de memoria fuera del montón de la JVM (es decir, el montón del sistema operativo), a través de Arena.allocate()y MemorySegment.allocateNative()que asignan memoria bruta directamente, mientras que Arena.close()y MemorySegment.close()se utilizan para desasignar/liberar el segmento de memoria.
Como interfaz de función externa , la clase java.lang.foreign.Linkerse ofrece para acceder entre funciones externas de Java (hacia y desde), java.lang.foreign.SymbolLookuppara recuperar la dirección de un símbolo en una biblioteca y java.lang.foreign.FunctionDescriptorpara modelar la firma de una función externa.
paquete org.wikipedia.examples ;import java.lang.foreign.Arena ; import java.lang.foreign.FunctionDescriptor ; import java.lang.foreign.Linker ; import java.lang.foreign.MemorySegment ; import java.lang.foreign.SymbolLookup ; import java.lang.foreign.ValueLayout ; import java.lang.invoke.MethodHandle ;public class ForeignFunctionExample { public static void main ( String [] args ) throws Throwable { Linker linker = Linker . nativeLinker (); SymbolLookup stdlib = linker . defaultLookup (); MethodHandle printf = linker . downcallHandle ( stdlib . findOrThrow ( "printf" ), FunctionDescriptor . of ( ValueLayout . JAVA_INT , ValueLayout . ADDRESS ) );MethodHandle strlen = linker . downcallHandle ( stdlib . findOrThrow ( "strlen" ), FunctionDescriptor . of ( ValueLayout . JAVA_LONG , ValueLayout . ADDRESS ) );try ( Arena arena = Arena . ofConfined ()) { MemorySegment formatString = arena . allocateUtf8String ( "Hello, %s!\n" ); MemorySegment argString = arena . allocateUtf8String ( "World" ); printf . invokeExact ( formatString , argString ); // imprime "Hello, World!" long len = ( long ) strlen . invokeExact ( formatString ); // len = 5 } } }Los diseños se modelan mediante java.lang.foreign.ValueLayout, que consta de:
Los tipos primitivos reciben ValueLayout.JAVA_*diseños cuando se asignan a sus tipos nativos, mientras que los tipos de puntero usan ValueLayout.ADDRESS(por ejemplo char*, int**o struct Point*).
Además, la constante MemorySegment.NULL(de tipo java.lang.foreign.MemorySegment) es un segmento nativo de longitud cero que representa una dirección nula ( nullptr), equivalente a MemorySegment.ofAddress(0).
Java ofrece métodos adicionales para expresar tipos nativos más complejos. Por ejemplo, los siguientes tipos nativos [ 22 ]
struct Punto { int x ; long y ; };unión Elección { flotante a ; entero b ; };Reciba los siguientes diseños:
import java.lang.foreign.MemoryLayout ; import java.lang.foreign.StructLayout ; import java.lang.foreign.UnionLayout ;StructLayoutpointLayout=MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("x"),MemoryLayout.paddingLayout(4),ValueLayout.JAVA_LONG.withName("y")};UnionLayoutchoiceLayout=MemoryLayout.unionLayout(ValueLayout.JAVA_FLOAT.withName("a"),ValueLayout.JAVA_INT.withName("b"));Alternatives
The Java Runtime Interface was created by Netscape as a precursor.[23]
Microsoft's proprietary implementation of a Java Virtual Machine (Visual J++) had a similar mechanism for calling native code from Java, called the Raw Native Interface (RNI). In addition, it offered a simple way to call existing native code that was not itself aware of Java, such as (but not limited to) the Windows API, (J/Direct). However, following the Sun–Microsoft litigation about this implementation, Visual J++ ceased to be maintained. RNI was simpler to use than JNI, because no management of the Java environment pointer was needed; instead, all Java objects were accessed directly. To facilitate this, a tool was used to generate header files from Java classes. Similarly, J/Direct was easier to use than using the necessary intermediate native library and JNI.
Java Native Access (JNA) is a community-developed library that provides Java programmers easy access to native shared libraries without using JNI. However, this requires the redistribution of the dependent jar library. The tradeoff is between JNI being more complex and JNA being slower, and not built-in like JNI.[24]
Historically, GCC (until GCC 7) offered the GNU Compiler for Java. This compiler offered the Compiled Native Interface (CNI), which allowed direct interoperability between C++ and Java. It provided an API that was heavily based off of JNI itself.[25] It claimed several advantages over JNI, as it represented Java classes directly as C++ classes.[26]
Desde Java 22, la API de funciones y memoria externas reemplaza a JNI, debido a su menor cantidad de código repetitivo y su interfaz simplificada. [ 20 ]
Véase también
Referencias
- ↑ "Descripción general de la interfaz nativa de Java" . La guía y especificación del programador de la interfaz nativa de Java . Consultado el 27 de diciembre de 2018 .
- ↑ "Descripción general de la interfaz nativa de Java SE 7" .
- ↑ "Usar el JNIEnv incorrecto" .
- ↑ "Proyecto Panamá (JDK 22)" .
- ^ "Especificación JNI (Java SE 25)" .
- ↑ "Invocación de programas en lenguaje ensamblador desde Java" . Java.net. 19 de octubre de 2006. Archivado del original el 30 de marzo de 2008. Consultado el 6 de octubre de 2007 .
- ↑ "Ejecutar aplicaciones Java desde programas en lenguaje ensamblador" . Java.net. 19 de octubre de 2006. Archivado del original el 11 de octubre de 2007. Consultado el 4 de octubre de 2007 .
- ↑ "Creando la JVM" .
- ↑ "Un análisis profundo del inicio de la JVM" .
- ↑ "Crear encabezado de JVM (código fuente)" .
- ↑ "Crear implementación de JVM (código fuente)" .
- ↑ "Tipos y estructuras de datos JNI" .
- ^ OpenJDK (13 de noviembre de 2024). "openjdk/jdk-jni.h" . github.com . AbiertoJDK.
- ↑ Benjamin Crawford Pierce. "Programación JNI en C++" . cis.upenn.edu . Universidad de Pensilvania . Consultado el 12 de junio de 2026 .
- ↑ "Descripción general del diseño de JNI" .
- ↑ "Código fuente JNI" .
- ↑ La API de invocación. Sun Microsystems. https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocation.html
- ↑ "Tipos y estructuras de datos JNI" .
- ↑ "Java: ¿Qué hace que las llamadas JNI sean lentas?" . Stack Overflow .
- 1 2 "JEP 454: API de memoria y funciones externas" . OpenJDK. 7 de abril de 2025.
- ↑ Oracle Corporation (15 de junio de 2026). "Interface Arena" . docs.oracle.com . Oracle Corporation.
- ↑ Oracle Corporation (15 de junio de 2026). "Enlazador de interfaz" . docs.oracle.com . Oracle Corporation.
- ↑ "Antecedentes históricos" .
- ↑ Zakusylo, Alexander. "Github también es la fuente original de JNA. Pruebas de velocidad de referencia JNA vs JNI" . Github . Github . Consultado el 30 de marzo de 2023 .
- ↑ Proyecto GNU. "11.17 Reflection" . gcc.gnu.org . Proyecto GNU . Consultado el 22 de mayo de 2026 .
- ↑ Preguntas frecuentes de GCJ – Proyecto GNU – Fundación del Software Libre (FSF)
Bibliografía
- Gordon, Rob (marzo de 1998). Essential JNI: Java Native Interface (1.ª ed.). Prentice Hall . pág. 498. ISBN 0-13-679895-0.
- Liang, Sheng (20 de junio de 1999). Java(TM) Native Interface: Programmer's Guide and Specification (1.ª ed.). Prentice Hall . pág. 320. ISBN 0-201-32577-2.
Enlaces externos
- Especificación de la API JNI 25.0 de Oracle
- Interfaz nativa de Java: Guía del programador y especificación
- JNI en Xcode de Apple
- Manejo de excepciones en JNI
- Tutorial sobre la API de funciones y memoria externas
- Java (lenguaje de programación)
- Java (plataforma de software)