質問

JNIメソッドを使用してJavaオブジェクトを構築する場合、JNI呼び出しAPIを使用して呼び出すJavaメソッドにパラメーターとして渡すために、そのメモリを管理するにはどうすればよいですか?

これが私が取り組んでいるものです:

free()よりも複雑なデストラクタメソッドを持つCオブジェクトがあります。このCオブジェクトはJavaオブジェクトに関連付けられ、アプリケーションがJavaオブジェクトで終了したら、Cオブジェクトはもう必要ありません。

私はそのようにJavaオブジェクトを作成しています(わかりやすくするためにエラーチェックを省略しています):

c_object = c_object_create ();
class = (*env)->FindClass (env, "my.class.name");
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V");
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object);

method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V");
(*env)->CallVoidMethod (env, other_class, method, instance);

では、 instance の処理が完了したので、それをどうすればよいですか?理想的には、ガベージコレクションをVMに任せたいと思います。 instance で完了したら、提供したポインターで c_object_destroy()を呼び出しても素晴らしいでしょう。これは可能ですか?

別の、しかし関連する質問は、このようなメソッドで作成するJavaエンティティのスコープに関係しています。上記の class constructor 、または method を手動で解放する必要がありますか? JNIのドキュメントは、適切なメモリ管理をテーマにしたイライラするほど曖昧です(私の判断では)。

役に立ちましたか?

解決

ネイティブリソース(オブジェクト、ファイル記述子など)を回収するための戦略がいくつかあります

  1. finalize()中にJNIメソッドを呼び出して、リソースを解放します。一部の人々はファイナライズの実装をお勧めします、基本的には本当に確信が持てませんネイティブリソースが解放されること。メモリなどのリソースの場合、これはおそらく問題ではありませんが、たとえば予測可能な時間にフラッシュする必要があるファイルがある場合、おそらくfinalize()は良い考えではありません。

  2. クリーンアップメソッドを手動で呼び出します。これは、リソースをクリーンアップする必要があることがわかっている時点がある場合に役立ちます。 JNIコードでDLLをアンロードする前に割り当てを解除する必要があるリソースがあったときに、このメソッドを使用しました。 DLLを後で再ロードできるようにするには、DLLをアンロードする前に、オブジェクトが実際に割り当て解除されていることを確認する必要がありました。 finalize()のみを使用すると、これは保証されません。これを(1)と組み合わせて、finalize()または手動で呼び出されたクリーンアップメソッドでリソースを割り当てることができます。 (おそらく、どのオブジェクトがクリーンアップメソッドを呼び出す必要があるかを追跡するには、WeakReferencesの正規マップが必要です。)

  3. おそらく PhantomReference を使用してこの問題を解決することもできますが、そのようなソリューションがどのように機能するかは正確にはわかりません。

実際、JNIのドキュメントについてはあなたに反対する必要があります。 JNI仕様を例外的に見つけましたローカルおよびグローバル参照の管理に関するセクションをさらに詳しく作成できたとしても、重要な問題のほとんどを明確にします。

他のヒント

JNI仕様では、誰が「所有」しているかという問題を扱っています。 JNIメソッドで作成されたJavaオブジェクトこちらローカルグローバルの参照を区別する必要があります。

JVMがネイティブコードへのJNI呼び出しを行うと、呼び出し中に作成されたすべてのオブジェクトを追跡するためのレジストリが設定されます。ネイティブコール中に作成された(つまり、JNIインターフェイス関数から返された)オブジェクトは、このレジストリに追加されます。このようなオブジェクトへの参照は、ローカル参照と呼ばれます。ネイティブメソッドがJVMに戻ると、ネイティブメソッドの呼び出し中に作成されたすべてのローカル参照が破棄されます。ネイティブメソッドの呼び出し中にJVMにコールバックする場合、制御がネイティブメソッドに戻るとき、ローカル参照は引き続き有効です。ネイティブコードから呼び出されたJVMがネイティブコードに別の呼び出しを行う場合、ローカル参照の新しいレジストリが作成され、同じルールが適用されます。

(実際、JNIインターフェースを使用して、JVMを作成し(JNIEnv *ポインターを受け取る)、コマンドラインで指定されたクラスを検索することにより、JVM実行可能ファイル(つまりjava.exe)を実装できます。そして、そのmain()メソッドを呼び出します。)

JNIインターフェースメソッドから返されるすべての参照はローカルです。つまり、通常の状況では、JNIに戻るときに参照が破棄されるため、JNIメソッドが返す参照を手動で割り当て解除する必要はありません。 JVMに戻る前に削除したいローカル参照がたくさんある場合など、「時期尚早に」それらを破棄したい場合があります。

グローバル参照は、NewGlobalRef()を使用して(ローカル参照から)作成されます。これらは特別なレジストリに追加され、手動で割り当てを解除する必要があります。グローバル参照は、ネイティブコードが複数のJNI呼び出しへの参照を保持する必要があるJavaオブジェクトにのみ使用されます。たとえば、Javaに伝播されるイベントをトリガーするネイティブコードがある場合などです。その場合、JNIコードは、イベントを受信するJavaオブジェクトへの参照を保存する必要があります。

メモリ管理の問題が少し明らかになることを期待してください。

Re:&quot;別の、しかし関連する質問&quot; ...コンテキスト。取得する実際のオブジェクト参照(jclass、jfieldID、jmethodIDではない)は、DeleteLocalRefでリリースする必要があります。

GCはインスタンスを収集しますが、ネイティブコードで割り当てられた非Javaヒープメモリを自動的に解放しません。 c_objectインスタンスを解放するには、クラスに明示的なメソッドが必要です。

これは、c_objectがリリースされているかどうかを確認し、リリースされていない場合はメッセージを記録するファイナライザーを使用することをお勧めするケースの1つです。

便利な手法は、JavaクラスコンストラクターでThrowableインスタンスを作成し、それをフィールドに格納する(またはフィールドをインラインで初期化する)ことです。ファイナライザは、クラスが適切に破棄されていないことを検出すると、スタックトレースを出力し、割り当てスタックを特定します。

推奨事項は、ストレートJNIを行わず、 gluegen または Swig (コードを生成し、静的にリンクできます)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top