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


115

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


JNIではなくJNAを選択した重要な要素の1つは、JNAがネイティブライブラリを変更する必要がなかったことです。Javaで作業できるようにするためだけにネイティブライブラリをカスタマイズする必要があったのは、私たちにとって大きなNOでした。
kvr

回答:


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

これらは私が遭遇した問題です。多分もっとあります。ただし、一般に、パフォーマンスはjnaとjniでそれほど変わらないため、JNAを使用できる場所ならどこでも使用できます。

編集

この答えは非常に人気があるようです。だからここにいくつかの追加があります:

  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に戻す方がよいと私は信じています。ネイティブ関数を頻繁に呼び出す必要がある場合、パフォーマンスへの影響が顕著になるためです。


6
JNAには多くのオーバーヘッドがあります。その便利さはタイムクリティカルでないコードで使用する価値があります
Gregory Pakosz

3
私はそのから直接引用し、AndroidのプロジェクトのためにBridJを使用する前に注意してお勧めするダウンロードページ:「BridJは、Android上で部分的に動作します/(SDKで)エミュレータアーム、および(未テスト)おそらく実際のデバイス上で。
kizzx2

1
@StockB:C ++オブジェクトを渡すか、C ++オブジェクトのメソッドを呼び出す必要があるAPIのライブラリがある場合、JNAはそれを行うことができません。これは、バニラCメソッドのみを呼び出すことができます。
Denis Tulskiy 2013年

2
私が理解している限り、JNIはグローバルJNIEXPORT関数しか呼び出せません。現在、JNIを使​​用するオプションとしてJavaCppを検討していますが、バニラJNIがこれをサポートしているとは思いません。私が見落としているバニラJNIを使​​用してC ++メンバー関数を呼び出す方法はありますか?
StockB 2013年


29

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

違いをベンチマークしたことはありませんが、設計上の理由から、少なくとも、ある状況ではJNAを使用した型変換がJNIを使​​用した場合よりもパフォーマンスが低下すると想定します。たとえば、配列を渡す場合、JNAは各関数呼び出しの最初にこれらをJavaからネイティブに変換し、関数呼び出しの最後に戻します。JNIを使​​用すると、配列のネイティブ「ビュー」が生成されたときに自分を制御できます。潜在的に配列の一部のビューのみが作成され、複数の関数呼び出しにわたってビューを保持し、最後にビューを解放して、必要かどうかを決定できます。変更を保持する(データをコピーする必要がある可能性がある)か、変更を破棄する(コピーは不要)。私は、Memoryクラスを使用してJNAの関数呼び出し全体でネイティブ配列を使用できることを知っていますが、これにはメモリのコピーも必要になります。これは、JNIでは不要な場合があります。違いは関係ないかもしれませんが、元の目標がネイティブコードでその一部を実装することによってアプリケーションのパフォーマンスを向上させることである場合、パフォーマンスの低いブリッジテクノロジーを使用することは最も明白な選択ではないようです。


8
  1. JNAが登場する数年前、または1.4より前のJREをターゲットにしているコードを書いている。
  2. 使用しているコードはDLL \ SOにありません。
  3. LGPLと互換性のないコードに取り組んでいます。

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


9
私は2について同意しません-静的ライブラリを動的ライブラリに変換するのは簡単です。それはabput私の質問を参照してくださいstackoverflow.com/questions/845183/...
JBを。

3
JNA 4.0から、JNAはLGPL 2.1とApache License 2.0の両方でデュアルライセンスされており、どちらを選択するかはあなた次第です
sbarber2

8

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


5
したがって、メソッド呼び出しの代わりに、メッセージを渡します。私はかなりの時間をJNIとJNAに、そしてBridJにも投資しましたが、すぐに、それらすべては少し恐ろしくなりすぎます。
Ustaman Sangat 2012

5

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

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


3

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呼び出しに変換します。


3

実際、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にあります。


2

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


1

私が何かを見逃していない限り、JNAとJNIの主な違いは、JNAではネイティブ(C)コードからJavaコードを呼び出せないということではないですか?


6
あなたはできる。C側の関数ポインターに対応するコールバッククラスを使用します。void register_callback(void(*)(const int)); public static native void register_callback(MyCallback arg1);にマップされます。MyCallbackは、com.sun.jna.Callbackを単一のメソッドvoid apply(int value)で拡張するインターフェースです。
Ustaman Sangat

@Augustoこれもコメントにすることができます
swiftBoy

0

私の特定のアプリケーションでは、JNIの方がはるかに使いやすいことがわかりました。シリアルポートとの間で連続的なストリームを読み書きする必要がありました。JNAの非常に複雑なインフラストラクチャを学ぶのではなく、6つの関数だけをエクスポートする専用DLLを使用してWindowsでネイティブインターフェイスをプロトタイプ化する方がはるかに簡単であることがわかりました。

  1. DllMain(Windowsとのインターフェースに必要)
  2. OnLoad(Javaコードがいつアタッチされるかを確認できるように、OutputDebugStringを実行するだけです)
  3. OnUnload(同上)
  4. 開く(ポートを開き、読み取りスレッドと書き込みスレッドを開始します)
  5. QueueMessage(書き込みスレッドによる出力のためにデータをキューに入れる)
  6. GetMessage(最後の呼び出し以降、読み取りスレッドによって受信されたデータを待機して返します)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.