1

JNIリファレンスはそれを言います

「ローカル参照は、ネイティブ メソッド呼び出しの間有効です。ネイティブ メソッドが戻った後、それらは自動的に解放されます。

ソース: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#global_local

私はここでちょっと迷っています。上記によると、明示的にNewGlobalRefを呼び出し、呼び出しから返されたオブジェクトをNewObjectに渡す必要があります。私はこれを試してみましたが、GC が作動すると、参照が収集されないようです (何かがまだ保持されているように)。次のプロジェクトを考えてみましょう: Main.java :

package lv.example;

import java.io.IOException;
import java.util.ArrayList;

class Main {

    public static void main(String[] args) {
        ArrayList<Object> store = new ArrayList<Object>();
            while(true) {
                Object d = null;
                try {
                    int c = System.in.read();
                    d = Dummy.getWeakGlobalRef();
                    if(c == 'c')
                        store.clear();
                    store.add(d);
                    System.out.println(d);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

    }

}

Dummy.java :

package lv.example;

class Dummy {

    static {

        System.loadLibrary("dummy");
    }
       native static Dummy getLocalRef();
       native static Dummy getGlobalRef();
       native static Dummy getWeakGlobalRef();

       @Override
       protected void finalize() throws Throwable {
            System.out.println("Finalized");
       }

}

libdummy.so には、ネイティブメソッドの実装が含まれています。

JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getLocalRef (JNIEnv *env, jclass cls) {
    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewObject(cls, id);
}


JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getGlobalRef (JNIEnv *env, jclass cls) {

    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewGlobalRef(env->NewObject(cls, id));
}


JNIEXPORT jobject JNICALL Java_lv_example_Dummy_getWeakGlobalRef (JNIEnv *env, jclass cls) {
    jmethodID id = env->GetMethodID(cls, "<init>", "()V");
    return env->NewWeakGlobalRef(env->NewObject(cls, id));
}

メイン ループが示す動作は私には奇妙に思えます: 1) getWeakGlobalRefまたはgetLocalRefを呼び出してArrayListをクリアすると、GC は作成したすべてのダミーオブジェクトを収集するようです。2) getGlobalRef を呼び出すと、ArrayListをクリアするかどうかに関係なく、オブジェクトは収集されません。l

ここで少し混乱しています。それは、ローカル参照をネイティブ メソッドから Java コードに戻すことができるということですか?

4

2 に答える 2

6

JVM は、ネイティブ コードがオブジェクトへの独自の参照を保持しているかどうかを知る必要があります。つまり、オブジェクトが削除された場合、C/C++ コードが引き続きオブジェクトにアクセスしようとする可能性があります (おそらく segfault が発生します)。グローバル参照を持つオブジェクトは、ネイティブ コードがそれらがまだ必要である可能性があることを示しているため、ガベージ コレクションされません。ローカル参照を持つオブジェクトは、参照が作成された関数がすでに Java に戻っているため、ガベージ コレクションを実行できます。弱いグローバル参照を持つオブジェクトはガベージ コレクションされる可能性があります。これは、ネイティブ コードが IsSameObject を使用して弱い参照を持つオブジェクトがガベージ コレクションされているかどうかを確認し、ガベージ コレクションされていない場合にのみオブジェクトを使用して正しく動作することを示しているためです。 .

再:

つまり、New*Ref はネイティブ コードのみを固定するためのものなのでしょうか? JVM は、Java 変数のインスタンスに割り当てるポイントまで「ローカル参照」を追跡しますか?

ローカル参照は、Java に戻るまで追跡されます。それを Java 変数に割り当てても参照は削除されませんが、そのオブジェクトのガベージ コレクションは個別に防止されます。返された値は、Java 変数に割り当てられるまでガベージ コレクションを防止するスタック上の参照を持っているため、ネイティブ コードが返された後に同じオブジェクトにアクセスしない限り、返されたオブジェクトがグローバル参照を持つ必要はありません。ガベージ コレクタは別のスレッドで実行されるため、ローカル参照がないと、JNI 関数の実行中に JVM がオブジェクトをガベージ コレクションする可能性があります。通常、New Ref/Delete Refを明示的に呼び出す必要はありません。

ネイティブ メソッドに渡されるすべての Java オブジェクト (JNI 関数呼び出しの結果として返されるものを含む) は、自動的にレジストリに追加されます (つまり、ローカル参照が与えられます)。

( https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#implementing_local_referencesから引用 )

あなたがそれをする必要があるかもしれないケース。

ネイティブ関数は、制御が元のスレッドで Java に返された後もオブジェクトを使用し続けるスレッドを生成します。

ネイティブ関数は、Java からの後続の呼び出しで使用するために、一部のグローバル変数に参照のコピーを格納します。

関数の実行中に参照を明示的に削除して、時間がかかる可能性のある関数の実行中にメモリを節約したい場合。

于 2015-11-07T14:32:07.173 に答える
1

はい。JNI ローカル参照はオブジェクトを参照します。そのオブジェクトへの参照を返すことができます。Java 側に戻ると、「ローカル参照」には意味がありません。

ガベージ コレクターは、一連のルート "ライブ" 参照から開始し、それらから到達可能なすべてのオブジェクトを "ライブ" としてマークすることによって機能します。他のすべてのオブジェクトは削除の対象となります。

JNI のローカル参照とグローバル参照は、ルート参照のセットに保持されます。ローカル参照は、それらを作成したネイティブ メソッドが戻ると自動的に削除されます。その前にローカル参照を削除できますし、削除する必要がある場合もあります。グローバル参照は、いつでもネイティブ コードによって明示的に作成および削除されます。

于 2015-11-08T17:55:28.370 に答える