web-dev-qa-db-fra.com

Utiliser JNI au lieu de JNA pour appeler du code natif?

JNA semble un peu plus facile à utiliser pour appeler du code natif que JNI. Dans quels cas utiliseriez-vous JNI sur JNA?

113
Marcus Leon
  1. JNA ne prend pas en charge le mappage des classes c ++, donc si vous utilisez la bibliothèque c ++, vous aurez besoin d'un wrapper jni
  2. Si vous avez besoin de beaucoup de copie de mémoire. Par exemple, vous appelez une méthode qui vous renvoie un tampon d'octets volumineux, vous y changez quelque chose, puis vous devez appeler une autre méthode qui utilise ce tampon d'octets. Cela vous obligerait à copier ce tampon de c vers Java, puis à le recopier à partir de Java vers c. Dans ce cas, jni gagnera en performances car vous pouvez conserver et modifier ce tampon en c, sans copier.

Ce sont les problèmes que j'ai rencontrés. Il y a peut-être plus. Mais en général, les performances ne sont pas si différentes entre jna et jni, donc partout où vous pouvez utiliser JNA, utilisez-le.

[~ # ~] modifier [~ # ~]

Cette réponse semble être assez populaire. Voici donc quelques ajouts:

  1. Si vous avez besoin de mapper C++ ou COM, il y a une bibliothèque par Oliver Chafic, créateur de JNAerator, appelée BridJ . C'est encore une jeune bibliothèque, mais elle a de nombreuses fonctionnalités intéressantes:
    • Interopérabilité dynamique C/C++/COM: appelez les méthodes C++, créez des objets C++ (et sous-classe des classes C++ à partir de Java!)
    • Mappages de type simples avec une bonne utilisation des génériques (y compris un modèle beaucoup plus agréable pour les pointeurs)
    • Prise en charge complète de JNAerator
    • fonctionne sur Windows, Linux, MacOS X, Solaris, Android
  2. En ce qui concerne la copie de mémoire, je crois que JNA prend en charge les ByteBuffers directs, de sorte que la copie de mémoire peut être évitée.

Donc, je pense toujours que dans la mesure du possible, il est préférable d'utiliser JNA ou BridJ, et de revenir à jni si les performances sont critiques, car si vous devez appeler fréquemment des fonctions natives, les performances sont perceptibles.

123
Denis Tulskiy

Il est difficile de répondre à une question aussi générique. Je suppose que la différence la plus évidente est qu'avec JNI, la conversion de type est implémentée du côté natif de la frontière Java/native, tandis qu'avec JNA, la conversion de type est implémentée en Java. Si vous vous sentez déjà assez à l'aise avec la programmation en C et devez implémenter vous-même du code natif, je suppose que JNI ne semble pas trop complexe. Si vous êtes un programmeur Java et que vous avez seulement besoin d'invoquer une bibliothèque native tierce, utiliser JNA est probablement le chemin le plus simple pour éviter les problèmes peut-être moins évidents avec JNI.

Bien que je n'ai jamais comparé de différences, je le ferais à cause de la conception, au moins supposons que la conversion de type avec JNA dans certaines situations fonctionnera moins bien qu'avec JNI. Par exemple, lors du passage de tableaux, JNA les convertit de Java en natif au début de chaque appel de fonction et de retour à la fin de l'appel de fonction. Avec JNI, vous pouvez contrôler vous-même quand un natif une "vue" du tableau est générée, ne créant potentiellement qu'une vue d'une partie du tableau, conservez la vue sur plusieurs appels de fonction et, à la fin, libérez la vue et décidez si vous souhaitez conserver les modifications (nécessitant éventuellement de copier le données) ou ignorer les modifications (aucune copie requise). Je sais que vous pouvez utiliser un tableau natif entre les appels de fonction avec JNA en utilisant la classe Memory, mais cela nécessitera également une copie de la mémoire, ce qui peut être inutile avec JNI. La différence peut ne pas être pertinent, mais si votre objectif initial est d'augmenter les performances des applications en implémentant certaines parties en code natif, l'utilisation d'une technologie de pont moins performante ne semble pas être le choix le plus évident.

29
jarnbjo
  1. Vous écrivez du code il y a quelques années avant la JNA ou vous ciblez un JRE antérieur à 1.4.
  2. Le code avec lequel vous travaillez n'est pas dans une DLL\SO.
  3. Vous travaillez sur du code incompatible avec LGPL.

C'est seulement ce que je peux trouver du haut de ma tête, bien que je ne sois pas un gros utilisateur non plus. Il semble également que vous pourriez éviter JNA si vous vouliez une meilleure interface que celle qu'ils fournissent, mais vous pouvez coder cela en Java.

8
stonemetal

Soit dit en passant, dans l'un de nos projets, nous avons conservé une très petite empreinte JNI. Nous avons utilisé des tampons de protocole pour représenter nos objets de domaine et n'avions donc qu'une seule fonction native pour relier Java et C (alors bien sûr, cette fonction C appelait un tas d'autres fonctions).

7
Ustaman Sangat

Ce n'est pas une réponse directe et je n'ai aucune expérience avec JNA mais, quand je regarde le Projets utilisant JNA et que je vois des noms comme SVNKit, IntelliJ IDEA, NetBeans IDE, etc., j'ai tendance à croire que c'est une bibliothèque assez décente.

En fait, je pense vraiment que j'aurais utilisé JNA au lieu de JNI quand je devais le faire car il semble en effet plus simple que JNI (qui a un processus de développement ennuyeux). Dommage, la JNA n'a pas été libérée à ce moment-là.

5
Pascal Thivent

J'ai en fait fait quelques benchmarks simples avec JNI et JNA.

Comme d'autres l'ont déjà souligné, la JNA est pour plus de commodité. Vous n'avez pas besoin de compiler ou d'écrire du code natif lorsque vous utilisez JNA. Le chargeur de bibliothèque natif de JNA est également l'un des meilleurs/des plus faciles à utiliser que j'ai jamais vu. Malheureusement, vous ne pouvez pas l'utiliser pour JNI, semble-t-il. (C'est pourquoi j'ai écrit ne alternative pour System.loadLibrary () qui utilise la convention de chemin d'accès de JNA et prend en charge le chargement transparent à partir du chemin de classe (c'est-à-dire les jars).)

Cependant, les performances de JNA peuvent être bien pires que celles de JNI. J'ai fait un test très simple qui a appelé une fonction d'incrémentation d'entier natif simple "return arg + 1;". Les benchmarks effectués avec jmh ont montré que les appels JNI à cette fonction sont 15 fois plus rapides que JNA.

Un exemple plus "complexe" où la fonction native résume un tableau entier de 4 valeurs montre toujours que les performances JNI sont 3 fois plus rapides que JNA. L'avantage réduit était probablement dû à la façon dont vous accédez aux tableaux dans JNI: mon exemple a créé des éléments et les a à nouveau libérés lors de chaque opération de sommation.

Le code et les résultats des tests peuvent être trouvés sur github .

3
user1050755

Si vous souhaitez des performances JNI mais que vous êtes intimidé par sa complexité, vous pouvez envisager d'utiliser des outils qui génèrent automatiquement des liaisons JNI. Par exemple, JANET (avertissement: je l'ai écrit) vous permet de mélanger Java et le code C++ dans un seul fichier source , et par exemple passer des appels de C++ à Java en utilisant la syntaxe standard Java. Par exemple, voici comment imprimer une chaîne C dans le Java:

native "C++" void printHello() {
    const char* helloWorld = "Hello, World!";
    `System.out.println(#$(helloWorld));`
}

JANET traduit ensuite le backtick-embedded Java en appels JNI appropriés.

3
Dawid Kurzyniec

J'ai étudié JNI et JNA pour la comparaison des performances parce que nous devions décider l'un d'entre eux d'appeler une DLL dans le projet et nous avions une contrainte de temps réel. Les résultats ont montré que JNI a des performances supérieures à JNA (environ 40 fois). Il existe peut-être une astuce pour de meilleures performances dans JNA, mais elle est très lente pour un exemple simple.

2
Mustafa Kemal

À moins que je manque quelque chose, la principale différence entre JNA et JNI n'est-elle pas qu'avec JNA, vous ne pouvez pas appeler le code Java à partir du code natif (C)?

1
Augusto