回答:
これらは私が遭遇した問題です。多分もっとあります。ただし、一般に、パフォーマンスはjnaとjniでそれほど変わらないため、JNAを使用できる場所ならどこでも使用できます。
編集
この答えは非常に人気があるようです。だからここにいくつかの追加があります:
そのため、可能な限りJNAまたはBridJを使用し、パフォーマンスが重要な場合はjniに戻す方がよいと私は信じています。ネイティブ関数を頻繁に呼び出す必要がある場合、パフォーマンスへの影響が顕著になるためです。
JNIEXPORT
関数しか呼び出せません。現在、JNIを使用するオプションとしてJavaCppを検討していますが、バニラJNIがこれをサポートしているとは思いません。私が見落としているバニラJNIを使用してC ++メンバー関数を呼び出す方法はありますか?
このような一般的な質問に答えることは困難です。最も明白な違いは、JNIでは型変換がJava /ネイティブボーダーのネイティブ側で実装されるのに対し、JNAでは型変換がJavaで実装されることです。Cでのプログラミングに既に慣れていて、ネイティブコードを自分で実装する必要がある場合は、JNIが複雑すぎないように思われます。Javaプログラマーであり、サードパーティのネイティブライブラリを呼び出すだけの場合は、おそらくJNIのそれほど明白ではない問題を回避するには、おそらくJNAを使用するのが最も簡単な方法です。
違いをベンチマークしたことはありませんが、設計上の理由から、少なくとも、ある状況ではJNAを使用した型変換がJNIを使用した場合よりもパフォーマンスが低下すると想定します。たとえば、配列を渡す場合、JNAは各関数呼び出しの最初にこれらをJavaからネイティブに変換し、関数呼び出しの最後に戻します。JNIを使用すると、配列のネイティブ「ビュー」が生成されたときに自分を制御できます。潜在的に配列の一部のビューのみが作成され、複数の関数呼び出しにわたってビューを保持し、最後にビューを解放して、必要かどうかを決定できます。変更を保持する(データをコピーする必要がある可能性がある)か、変更を破棄する(コピーは不要)。私は、Memoryクラスを使用してJNAの関数呼び出し全体でネイティブ配列を使用できることを知っていますが、これにはメモリのコピーも必要になります。これは、JNIでは不要な場合があります。違いは関係ないかもしれませんが、元の目標がネイティブコードでその一部を実装することによってアプリケーションのパフォーマンスを向上させることである場合、パフォーマンスの低いブリッジテクノロジーを使用することは最も明白な選択ではないようです。
それは私が頭の上から思いつくことができることだけですが、私はどちらのヘビーユーザーでもありません。また、JNAが提供するよりも優れたインターフェースが必要な場合はJNAを回避する可能性がありますが、Javaでそれをコード化できます。
ちなみに、私たちのプロジェクトの1つでは、非常に小さなJNIフットプリントを維持しました。ドメインオブジェクトを表すためにプロトコルバッファーを使用したため、JavaとCをブリッジするネイティブ関数は1つだけでした(もちろん、C関数は他の関数の束を呼び出します)。
JNIのパフォーマンスが必要だが、その複雑さに気が進まない場合は、JNIバインディングを自動的に生成するツールの使用を検討してください。たとえば、JANET(免責事項:私が作成しました)では、JavaとC ++のコードを1つのソースファイルに混在させることができます。たとえば、標準のJava構文を使用してC ++からJavaに呼び出しを行うことができます。たとえば、C文字列をJava標準出力に出力する方法は次のとおりです。
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
JANETは、バックティックが埋め込まれたJavaを適切なJNI呼び出しに変換します。
実際、JNIとJNAを使用していくつかの簡単なベンチマークを行いました。
他の人がすでに指摘したように、JNAは便宜上のものです。JNAを使用する場合、ネイティブコードをコンパイルまたは作成する必要はありません。JNAのネイティブライブラリローダーも、今まで見た中で最も使いやすい/最も使いやすいものの1つです。残念ながら、JNIには使用できません。(それが、JNAのパス規則を使用し、クラスパス(つまりjar)からのシームレスなロードをサポートするSystem.loadLibrary()の代替を作成した理由です。)
ただし、JNAのパフォーマンスはJNIのパフォーマンスよりもはるかに悪い場合があります。私は、単純なネイティブ整数インクリメント関数「return arg + 1;」を呼び出す非常に単純なテストを行いました。jmhで行われたベンチマークは、その関数へのJNI呼び出しがJNAの15倍高速であることを示しました。
ネイティブ関数が4つの値の整数配列を合計する「より複雑な」例でも、JNIのパフォーマンスはJNAの3倍高速であることがわかりました。利点が減ったのは、おそらくJNIで配列にアクセスする方法が原因でした。私の例では、いくつかの要素を作成し、各加算操作中に再度解放しました。
コードとテスト結果はgithubにあります。
私が何かを見逃していない限り、JNAとJNIの主な違いは、JNAではネイティブ(C)コードからJavaコードを呼び出せないということではないですか?
私の特定のアプリケーションでは、JNIの方がはるかに使いやすいことがわかりました。シリアルポートとの間で連続的なストリームを読み書きする必要がありました。JNAの非常に複雑なインフラストラクチャを学ぶのではなく、6つの関数だけをエクスポートする専用DLLを使用してWindowsでネイティブインターフェイスをプロトタイプ化する方がはるかに簡単であることがわかりました。