JNIEnv C ), des applications natives (programmes spécifiques à une plateforme matérielle et un système d'exploitation ) et des bibliothèques écrites dans d'autres langages tels que C , C++ et assembleur . Java 22 introduit l' API Foreign Function and Memory , qui peut être considérée comme la successeure de l'interface native Java.
bibliothèque de classes standard Java ne prend pas en charge certaines fonctionnalités spécifiques à la plateforme ou à une bibliothèque de programmes particulière. JNI est également utilisé pour modifier une application existante (écrite dans un autre langage de programmation) afin de la rendre accessible aux applications Java. De nombreuses classes de la bibliothèque standard dépendent de JNI pour fournir des fonctionnalités aux développeurs et aux utilisateurs, telles que les API d'entrée/sortie de fichiers et audio. Seules les applications et les applets signées peuvent invoquer JNI.Le framework JNI permet au code natif de manipuler et d'utiliser des objets Java . Une méthode native (indiquée par le mot-clé `native` interface native Java AWT , disponible depuis J2SE 1.3.
JNI permet également un accès direct à l'assembleur , sans passer par un pont C. L'accès aux applications Java depuis l'assembleur est possible de la même manière.
L'interface JNI est le point d'entrée entre les langages utilisés pour démarrer la JVM.
Types de cartographie
Les types Java sont les types primitifs par défaut disponibles sur Java, tandis que les signatures de type JVM sont des identificateurs utilisés par la JVM pour identifier la correspondance de type sur Java, tandis que les types natifs sont des types définis par JNI lui-même pour correspondre à des types natifs. Pour les types primitifs, les typedefs suivants sont fournis :
| Type Java | typedef natif | Description | signature de type JVM | Type natif le plus proche | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
boolean | jboolean | 8 bits non signés | Z | bool | |||||||||||||||||||||||||||||
byte | jbyte | signé 8 bits | signature de type JVM fait référence à la classe spécifiée de manière unique par ce nom ; par exemple, la signature ( dans les fichiers de classe ) fait référence à la classe . Le préfixe à la signature désigne un tableau de ce type ; par exemple, fait référence à , et fait référence à , jusqu’à un maximum de 255 dimensions.Lfully-qualified-class;Ljava.lang.String;Ljava/lang/String;conversion de type . Cependant, la correspondance entre les chaînes et les tableaux Java et les chaînes et tableaux natifs est différente ; par exemple, `int` |
De plus, les types opaques suivants sont utilisés comme poignées :
| méta-objet de classe Java | Gestionnaire JNI |
|---|---|
| Champ de classe | jfieldID |
| Méthode de classe | jmethodID |
Notez cependant que jfieldIDet jmethodIDne sont que des poignées opaques et ne correspondent pas à java.lang.reflect.Field/ java.lang.reflect.Method.
Conception
La communication entre le code natif et le code Java est assurée par le JNI, qui agit comme une ABI .
Le envpointeur est une structure qui contient l'interface avec la JVM. Il inclut toutes les fonctions nécessaires pour interagir avec la JVM et manipuler les objets Java. Tout ce que le code Java peut faire peut être fait avec ce pointeur JNIEnv. L'argument objest une référence à l'objet Java dans lequel cette méthode native a été déclarée.
Les types de données natifs peuvent être mappés vers/depuis les types de données Java. Pour les types composés, tels que les objets, les tableaux et les chaînes de caractères , le code natif doit explicitement convertir les données en appelant des méthodes dans le JNIEnv.
Un pointeur d'environnement JNI JNIEnv*est passé en argument à chaque fonction native mappée à une méthode Java, permettant ainsi l'interaction avec l'environnement JNI au sein de la méthode native. Ce pointeur d'interface JNI peut être stocké, mais reste valide uniquement dans le thread courant. Les autres threads doivent d'abord s'attacher AttachCurrentThread()à la JVM et obtenir un pointeur d'interface JNI. Une fois attaché, un thread natif fonctionne comme un thread Java classique exécuté dans une méthode native. Le thread natif reste attaché à la JVM jusqu'à ce qu'il appelle DetachCurrentThread()la méthode de détachement.
Le framework JNI ne fournit aucun système de nettoyage automatique de la mémoire pour les ressources mémoire non-JVM allouées par le code exécuté côté natif. Par conséquent, il incombe au code natif de libérer explicitement toutes les ressources acquises.
Sur les plateformes Linux et Solaris, si le code natif s'enregistre comme gestionnaire de signaux, il peut intercepter les signaux destinés à la JVM. Une chaîne de responsabilité permet l'interopérabilité entre le code natif et la JVM. Sur les plateformes Windows, la gestion structurée des exceptions (SEH) permet d'encapsuler le code natif dans des blocs SEH tryafin catchde capturer les interruptions logicielles générées par la machine (CPU/FPU), telles que les déréférencements de pointeurs nuls et les divisions par zéro, et de gérer ces situations avant que l'interruption ne soit propagée à la JVM (c'est-à-dire au code Java).
L' encodage pour NewStringUTF()les fonctions GetStringUTFLength()` get_to ...GetStringUTFChars()ReleaseStringUTFChars()GetStringUTFRegion()NewString()GetStringLength()GetStringChars()ReleaseStringChars()GetStringRegion()GetStringCritical()ReleaseStringCritical()" ); }
En Java, la fonction est alors appelée comme suit :
Performance
JNI encourt des frais généraux et une perte de performance considérables dans certaines circonstances :
- Les appels de fonction aux méthodes JNI sont coûteux, surtout lorsqu'on appelle une méthode de manière répétée.
- Les méthodes natives ne sont pas intégrées par la JVM, et la méthode ne peut pas non plus être compilée JIT , car elle est déjà compilée.
- Un tableau Java peut être copié pour être utilisé dans le code natif, puis recopié ultérieurement. Le coût est linéaire par rapport à la taille du tableau.
- Si la méthode reçoit un objet en paramètre ou doit effectuer un rappel, elle sera probablement amenée à interroger la JVM. L'accès aux champs, méthodes et types Java depuis du code natif fait appel à la réflexion Java . Les signatures sont spécifiées sous forme de chaînes de caractères et interrogées auprès de la JVM. Cette méthode est à la fois lente et sujette aux erreurs.
- En Java, les chaînes de caractères sont des objets possédant une longueur et étant encodées. Accéder à une chaîne ou en créer une peut nécessiter une copie.
API de fonction étrangère et de mémoire
Java 22 a introduit l'API Foreign Functions and Memory (FFM), la gestion de la mémoire par régions . La classe `ForeignFunctions` java.lang.foreign.MemorySegmentmodélise un segment de mémoire contigu pouvant se trouver à l'intérieur ou à l'extérieur du tas JVM, et un ` ForeignFunctions` java.lang.foreign.MemorySegmentest alloué à l'aide d'un ` Remote` java.lang.foreign.Arenaqui contrôle la durée de vie de la région de mémoire sous-jacente à son segment alloué.
L' java.lang.foreign.Arenaoffre comprend les types d'arènes suivants :
| Type d'arène | Méthode d'usine | Durée de vie limitée | Fermeture explicite | Accessible depuis plusieurs threads |
|---|---|---|---|---|
| Mondial | Arena.global() | Non | Non | Oui |
| Automatique | Arena.ofAuto() | Oui | Non | Oui |
| Confiné | Arena.ofConfined() | Oui | Oui | Non |
| Commun | Arena.ofShared() | Oui | Oui | Oui |
En un sens, l'API Java Foreign Function and Memory permet une allocation directe de mémoire en dehors du tas JVM (c'est-à-dire le tas du système d'exploitation), via Arena.allocate()et MemorySegment.allocateNative()qui allouent directement de la mémoire brute, tandis que Arena.close()et MemorySegment.close()sont utilisés pour désallouer/libérer le segment de mémoire.
En tant qu'interface de fonction étrangère , les classes java.lang.foreign.Linkersont proposées pour accéder aux fonctions étrangères de Java (vers et depuis), java.lang.foreign.SymbolLookuppour récupérer l'adresse d'un symbole dans une bibliothèque et java.lang.foreign.FunctionDescriptorpour modéliser la signature d'une fonction étrangère.
Les mises en page sont modélisées par java.lang.foreign.ValueLayout, qui se compose de :
| Taper | Nom | Description |
|---|---|---|
java.lang.foreign.AddressLayout | ADDRESS | Une disposition d'adresse de même taille quesize_t |
java.lang.foreign.AddressLayout | ADDRESS_UNALIGED | Une disposition d'adresse non alignée de même taille quesize_t |
java.lang.foreign.ValueLayout.OfBoolean | JAVA_BOOLEAN | Une constante de mise en page de valeur de même taille qu'une valeur Javaboolean |
java.lang.foreign.ValueLayout.OfByte | JAVA_BYTE | Une constante de mise en page de valeur de même taille qu'une valeur Javabyte |
java.lang.foreign.ValueLayout.OfChar | JAVA_CHAR | Une constante de mise en page de valeur de même taille qu'une valeur Javachar |
java.lang.foreign.ValueLayout.OfChar | JAVA_CHAR_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javachar |
java.lang.foreign.ValueLayout.OfShort | JAVA_SHORT | Une constante de mise en page de valeur de même taille qu'une valeur Javashort |
java.lang.foreign.ValueLayout.OfShort | JAVA_SHORT_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javashort |
java.lang.foreign.ValueLayout.OfInt | JAVA_INT | Une constante de mise en page de valeur de même taille qu'une valeur Javaint |
java.lang.foreign.ValueLayout.OfInt | JAVA_INT_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javaint |
java.lang.foreign.ValueLayout.OfLong | JAVA_LONG | Une constante de mise en page de valeur de même taille qu'une valeur Javalong |
java.lang.foreign.ValueLayout.OfLong | JAVA_LONG_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javalong |
java.lang.foreign.ValueLayout.OfFloat | JAVA_FLOAT | Une constante de mise en page de valeur de même taille qu'une valeur Javafloat |
java.lang.foreign.ValueLayout.OfFloat | JAVA_FLOAT_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javafloat |
java.lang.foreign.ValueLayout.OfDouble | JAVA_DOUBLE | Une constante de mise en page de valeur de même taille qu'une valeur Javadouble |
java.lang.foreign.ValueLayout.OfDouble | JAVA_DOUBLE_UNALIGNED | Une mise en page de valeurs non alignées de même taille qu'une Javadouble |
Les types primitifs reçoivent ValueLayout.JAVA_*des mises en page lorsqu'ils sont mappés à leurs types natifs, tandis que les types pointeurs utilisent ValueLayout.ADDRESS(par exemple char*, int**ou struct Point*).
De plus, la constante MemorySegment.NULL(de type java.lang.foreign.MemorySegment) est un segment natif de longueur nulle représentant une adresse nulle ( nullptr), équivalent à MemorySegment.ofAddress(0).
Java offre des méthodes supplémentaires pour exprimer des types natifs plus complexes. Par exemple, les types natifs suivants
Alternatives
The Java Runtime Interface was created by Netscape as a precursor.
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.
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. It claimed several advantages over JNI, as it represented Java classes directly as C++ classes.
Depuis Java 22, l'API Foreign Function and Memory remplace JNI, en raison de sa réduction du code répétitif et de son interface simplifiée.