web-dev-qa-db-ja.com

JNAの代わりにJNIを使​​用してネイティブコードを呼び出しますか?

JNAは、JNIと比較してネイティブコードを呼び出すのにかなり使いやすいようです。どのような場合にJNA over JNAを使用しますか?

113
Marcus Leon
  1. JNAはc ++クラスのマッピングをサポートしていないため、c ++ライブラリを使用している場合はjniラッパーが必要です。
  2. 大量のメモリコピーが必要な場合。たとえば、1つのメソッドを呼び出して大きなバイトバッファーを返し、その中の何かを変更した後、このバイトバッファーを使用する別のメソッドを呼び出す必要があります。これは、このバッファーをcからJavaにコピーしてから、Java= cにコピーして戻す必要があります。この場合、cでこのバッファーを保持および変更できるため、jniのパフォーマンスが向上します。コピーせずに。

これらは私が遭遇した問題です。他にもあるかもしれません。しかし、一般的に、パフォーマンスはjnaとjniの間でそれほど違いはないので、JNAを使用できるところならどこでも使用してください。

[〜#〜] edit [〜#〜]

この答えは非常に人気があるようです。そのため、いくつかの追加があります。

  1. C++またはCOMをマップする必要がある場合は、JNAeratorの作成者であるOliver Chaficによる BridJ というライブラリがあります。まだ若いライブラリですが、多くの興味深い機能があります:
    • 動的C/C++/COM相互運用:C++メソッドを呼び出し、C++オブジェクトを作成します(およびJavaからC++クラスをサブクラス化します!)
    • ジェネリックを適切に使用した単純な型マッピング(ポインターのより優れたモデルを含む)
    • JNAeratorの完全サポート
    • windows、Linux、MacOS X、Solaris、Androidで動作します
  2. メモリコピーに関しては、JNAは直接ByteBufferをサポートしているため、メモリコピーを回避できます。

したがって、ネイティブ機能を頻繁に呼び出す必要がある場合はパフォーマンスのヒットが顕著になるため、可能な限り、JNAまたはBridJを使用し、パフォーマンスが重要な場合はjniに戻すことをお勧めします。

123
Denis Tulskiy

このような一般的な質問に答えることは困難です。最も明らかな違いは、JNIでは、型変換がJava /ネイティブ境界のネイティブ側で実装され、JNAでは、型変換がJavaで実装されることです。すでにCでのプログラミングに非常に満足しており、自分でネイティブコードを実装する必要がある場合は、JNIがあまり複雑に見えないと思います。あなたがJavaプログラマであり、サードパーティのネイティブライブラリを呼び出すだけでよい場合、JNAを使用するのが、JNIでそれほど明白ではない問題を回避する最も簡単な方法です。

違いをベンチマークしたことは一度もありませんが、少なくともデザインによっては、少なくとも状況によってはJNAでの型変換のパフォーマンスがJNIよりも悪いと思います。たとえば、配列を渡すとき、JNAは各関数呼び出しの開始時にこれらをJavaからネイティブに変換し、関数呼び出しの終了時に元に戻します。JNIを使​​用すると、配列の「ビュー」が生成され、潜在的に配列の一部のビューを作成し、複数の関数呼び出しにわたってビューを保持し、最後にビューを解放し、変更を保持するかどうかを決定します(メモリクラスを使用してJNAで関数呼び出し間でネイティブ配列を使用できることはわかっていますが、これにはJNIでは不要なメモリコピーも必要になります。関連性はありますが、本来の目標がネイティブコードでアプリケーションのパフォーマンスを向上させることである場合、パフォーマンスの低いブリッジテクノロジーを使用することは最も明白な選択肢ではないようです。

29
jarnbjo
  1. 数年前にJNAが存在する前にコードを書いているか、1.4より前のJREをターゲットにしています。
  2. 作業しているコードはDLL\SOにありません。
  3. LGPLと互換性のないコードに取り組んでいます。

私はどちらかをヘビーユーザーではありませんが、頭のてっぺんから思いつくことができるのはそれだけです。また、JNAが提供するインターフェースよりも優れたインターフェースが必要であるが、Javaでそれをコード化できる場合は、JNAを避けるかもしれません。

8
stonemetal

ところで、私たちのプロジェクトの1つでは、非常に小さなJNIフットプリントを維持しました。ドメインオブジェクトを表すためにプロトコルバッファを使用したため、JavaとCをブリッジするネイティブ関数が1つだけでした(もちろん、C関数は他の多くの関数を呼び出します)。

7
Ustaman Sangat

それは直接的な答えではなく、JNAの経験はありませんが、 JNAを使用するプロジェクト を見て、SVNKit、IntelliJ IDEA、NetBeans IDEなどの名前を見ると、それが信じられがちですかなりまともなライブラリ。

実際、JNI(退屈な開発プロセスがある)よりも単純に見えるので、必要に応じてJNIの代わりにJNAを使用したと思います。残念なことに、JNAは現時点ではリリースされていません。

5
Pascal Thivent

実際に、JNIとJNAでいくつかの簡単なベンチマークを行いました。

他の人がすでに指摘しているように、JNAは便宜上のものです。 JNAを使用する場合、ネイティブコードをコンパイルまたは記述する必要はありません。 JNAのネイティブライブラリローダーは、今まで見た中で最も使いやすい/使いやすいものの1つです。悲しいことに、JNIには使用できないようです。 (だから私は System.loadLibrary()の代替 を書きました。これはJNAのパス規約を使用し、クラスパス(つまり、jar)からのシームレスなロードをサポートします。)

ただし、JNAのパフォーマンスは、JNIのパフォーマンスよりもはるかに悪い場合があります。単純なネイティブ整数インクリメント関数「return arg + 1;」と呼ばれる非常に単純なテストを行いました。 jmhで行ったベンチマークは、その関数へのJNI呼び出しがJNAより15倍速いことを示しました。

ネイティブ関数が4つの値の整数配列を合計する「複雑な」例は、JNIのパフォーマンスがJNAの3倍速いことを示しています。利点の減少は、おそらくJNIで配列にアクセスする方法が原因でした。私の例では、いくつかの要素を作成し、各加算操作中に再度リリースしました。

コードとテスト結果は github にあります。

3
user1050755

JNIのパフォーマンスが必要であるが、その複雑さにdaしている場合は、JNIバインディングを自動的に生成するツールの使用を検討できます。たとえば、 [〜#〜] janet [〜#〜] (免責事項:私が書いた)を使用すると、単一のソースファイルにJavaとC++コードを混在させることができます。標準のJava構文を使用して、C++からJavaを呼び出します。たとえば、C文字列をJava標準出力に出力する方法は次のとおりです。

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

JANETは、バックティックに埋め込まれたJavaを適切なJNI呼び出しに変換します。

3
Dawid Kurzyniec

パフォーマンスの比較のためにJNIとJNAを調査しました。プロジェクトのdllを呼び出すためにそれらの1つを決定する必要があり、リアルタイムの制約があったためです。結果は、JNIのパフォーマンスがJNAよりも大きいことを示しています(約40倍)。 JNAのパフォーマンスを向上させるためのコツがあるかもしれませんが、簡単な例では非常に遅いです。

2
Mustafa Kemal

何かが足りない場合を除き、JNAとJNIの主な違いは、JNAを使用してJavaネイティブ(C)コードからコードを呼び出せないことですか?

1
Augusto