76

現在、CUDA 機能を使用する Java アプリケーションを作成しようとしています。CUDA と Java の間の接続は正常に機能しますが、別の問題が発生したので、それについての私の考えが正しいかどうかを尋ねたいと思いました。

Java からネイティブ関数を呼び出すと、関数にデータが渡され、関数が何かを計算して結果を返します。最初の関数がこの結果への参照 (ポインター) を返して、それを JNI に渡して、結果をさらに計算する別の関数を呼び出すことは可能ですか?

私の考えは、データを GPU メモリに残し、その参照を渡すだけで他の関数が使用できるようにすることで、GPU との間でデータをコピーすることによるオーバーヘッドを削減することでした。

しばらく試した後、アプリケーションの終了後 (この場合は C 関数の終了時) にポインターが削除されるため、これは不可能であると自分で考えました。これは正しいです?それとも、解決策を見るためにCで悪いのですか?

編集:まあ、質問を少し拡張する (またはより明確にする): JNI ネイティブ関数によって割り当てられたメモリは、関数が終了したときに解放されますか? または、JNI アプリケーションが終了するまで、または手動で解放するまで、引き続きアクセスできますか?

ご意見ありがとうございます:)

4

7 に答える 7

50

次のアプローチを使用しました。

JNI コードで、必要なオブジェクトへの参照を保持する構造体を作成します。この構造体を最初に作成するときは、Java へのポインターをlong. 次に、Java からこれをパラメータとして任意のメソッドを呼び出すだけlongで、C ではそれを構造体へのポインタにキャストします。

構造体はヒープにあるため、異なる JNI 呼び出し間でクリアされません。

(long)&address;編集: address は静的変数であるため、long ptr = を使用できないと思います。Gunslinger47 が提案した方法で使用します。つまり、(new または malloc を使用して) クラスまたは構造体の新しいインスタンスを作成し、そのポインターを渡します。

于 2009-10-27T23:30:43.023 に答える
17

C ++では、メモリを割り当て/解放したい任意のメカニズム(スタック、malloc / free、new / delete、またはその他のカスタム実装)を使用できます。唯一の要件は、1つのメカニズムでメモリのブロックを割り当てた場合、同じメカニズムでそれを解放する必要があるためfree、スタック変数を呼び出すこともdeletemallocedメモリを呼び出すこともできません。

JNIには、JVMメモリを割り当て/解放するための独自のメカニズムがあります。

  • NewObject / DeleteLocalRef
  • NewGlobalRef / DeleteGlobalRef
  • NewWeakGlobalRef / DeleteWeakGlobalRef

PopLocalFrameこれらは同じルールに従いますが、唯一の落とし穴は、ネイティブメソッドが終了するときに、明示的に、または暗黙的に、ローカル参照を「まとめて」削除できることです。

JNIはメモリをどのように割り当てたかを知らないため、関数の終了時にメモリを解放することはできません。まだC++を記述しているため、スタック変数は明らかに破棄されますが、GPUメモリは引き続き有効です。

唯一の問題は、その後の呼び出しでメモリにアクセスする方法です。その後、Gunslinger47の提案を使用できます。

JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
    MyClass* pObject = new MyClass(...);
    return (long)pObject;
}

JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
    MyClass* pObject = (MyClass*)lp;
    ...
}
于 2010-03-19T12:42:30.097 に答える
11

Java はポインターをどう処理するかわかりませんが、ネイティブ関数の戻り値からポインターを格納し、それを別のネイティブ関数に渡して処理できるようにする必要があります。C ポインターは、コアの数値にすぎません。

別の貢献者は、指定されたグラフィックス メモリが JNI 呼び出しの間にクリアされるかどうか、および回避策があるかどうかを教えてくれる必要があります。

于 2009-10-27T18:00:04.287 に答える
9

この質問はすでに公式に回答されていることは知っていますが、解決策を追加したいと思います。ポインターを渡そうとする代わりに、ポインターを Java 配列 (インデックス 0) に入れ、それを JNI に渡します。JNI コードは、GetIntArrayRegion/を使用して配列要素を取得および設定できますSetIntArrayRegion

私のコードでは、ファイル記述子 (オープン ソケット) を管理するためにネイティブ レイヤーが必要です。Java クラスはint[1]配列を保持し、それをネイティブ関数に渡します。ネイティブ関数は、それを使って何でも (get/set)、結果を配列に戻すことができます。

于 2011-11-28T08:18:35.753 に答える
7

ネイティブ関数内で動的に (ヒープ上で) メモリを割り当てている場合、メモリは削除されません。つまり、ポインターや静的変数などを使用して、ネイティブ関数への異なる呼び出し間で状態を保持できます。

別の方法で考えてみてください。別の C++ プログラムから呼び出された関数呼び出しを安全に保持するにはどうすればよいでしょうか? ここでも同じことが当てはまります。関数が終了すると、その関数呼び出しのスタックにあるものはすべて破棄されます。ただし、明示的に削除しない限り、ヒープ上のものはすべて保持されます。

簡単な答え: 呼び出し元の関数に返す結果の割り当てを解除しない限り、後で再入できるようになります。完了したら、必ずクリーンアップしてください。

于 2009-10-27T22:19:33.680 に答える