JNIローカル参照とグローバル参照

11390 ワード

1.概要:
JNIでは、ローカルリファレンス(local references)、グローバルリファレンス(global references)、弱いグローバルリファレンス(weak global references.)の3つのわかりにくいリファレンスがサポートされています.
  • ローカルリファレンスとグローバルリファレンスには異なるライフサイクルがあり、ローカルリファレンスは自動的に解放されますが、グローバルリファレンスと弱いグローバルリファレンスはプログラマによって解放される必要があります.
  • ローカル参照およびグローバル参照は、参照するオブジェクトがゴミ回収されることを阻止する一方、弱いグローバル参照は、参照するオブジェクトがゴミ回収されることを可能にする
  • .
  • は、すべての参照がすべてのコンテキスト(context)で使用できるわけではありません.たとえば、ローカルメソッドで作成され、その後返されるローカル参照は不正です.

  • 2.ローカル参照とグローバル参照(Local and Global References)
    ローカルリファレンスとグローバルリファレンスは何ですか?何が違うの?一連の例を用いて彼らを説明する.
    2.1.ローカルリファレンス(Local References)
    多くのJNI関数はローカル参照を作成します.たとえば、JNI関数NewObjectは新しいインスタンスを作成し、このインスタンスのローカル参照を返します.ローカル参照は、そのローカルメソッドを作成する動的コンテキストとローカルメソッドの呼び出しでのみ有効です.ローカルメソッドの実行中に作成されたすべてのローカルリファレンスは、ローカルメソッドが返されると解放されます.ローカルメソッドにローカル参照を静的変数に格納することを禁止し、後続の呼び出しで同じ参照を使用します.次の例を参照してください.
    /* This code is illegal */
    jstring
    MyNewString(JNIEnv *env, jchar *chars, jint len)
    {
    	static jclass stringClass = NULL;
    	jcharArray elemArr;
    	//static jmethodID cid = NULL;
    	jmethodID cid = NULL;
    	jstring result;
    	ALOGI(" MyNewString::chars = %s, len=%d
    ", chars,len); if (stringClass == NULL) { stringClass = env->FindClass("java/lang/String"); if (stringClass == NULL) { return NULL; /* exception thrown */ } } /* It is wrong to use the cached stringClass here, because it may be invalid. */ /* Note that cid is a static variable */ //if (cid == NULL) { /* Get the method ID for the String constructor */ cid =env->GetMethodID(stringClass,"<init>", "([C)V"); if (cid == NULL) { return NULL; /* exception thrown */ } //} /* Create a char[] that holds the string characters */ elemArr = env->NewCharArray(len); if (elemArr == NULL) { return NULL; /* exception thrown */ } env->SetCharArrayRegion(elemArr, 0, len, chars); for(int i=0;i<len;i++){ ALOGI(" MyNewString::elemArr[%d] = %c
    ", i,*(elemArr+i)); } /* Construct a java.lang.String object */ result = (jstring)env->NewObject(stringClass, cid, elemArr); //ALOGI(" MyNewString::result = %s
    ", result); /* Free local references */ env->DeleteLocalRef(elemArr); //env->DeleteLocalRef(stringClass); return result; }

    このコードの静的変数stringClassを使用する目的は、「FindClass(env,「java/lang/String」)の繰り返し呼び出しを避けることです.しかしFindClassはjavaを返します.lang.Stringクラスオブジェクトのローカル参照は、なぜ間違っているのかを説明するために、ローカルメソッドsimplejni_を参照してください.referencesExampleでの関数呼び出しの実装:
    void simplejni_referencesExample(JNIEnv *env, jobject obj){
             char *c_str = ...;
          ...
          return MyNewString(c_str ...);
    }

    simplejni_でreferencesExampleが戻った後、VMはsimplejniで解放されます.referencesExampleが作成したstringClass変数を含むすべてのローカルリファレンスは、今後MyNewStringの呼び出しに不正なローカルリファレンスを使用し、メモリ破損(memory corruption)またはシステムクラッシュ(system crashes)を引き起こす.たとえば、javaでローカルメソッドを2回呼び出すコードは次のとおりです.
    ...
    ... = C.referencesExample(); // The first call is perhaps OK.
    ... = C.referencesExample(); // This would use an invalid local reference. the system will crash.
    ...
    

    ローカルリファレンスがローカルメソッドで返されるときに自動的に解放されるほか、JNI関数DeleteLocalRefを使用してアクティブに解放され、MyNewStringで「env->DeleteLocalRef(elemArr);「elemArr」配列をすぐに回収します.DeleteLocalRef(elemArr)を呼び出してelemArrを解放しない場合、ローカルメソッドがMyNewStringを呼び出して戻ってきた後にのみ解放されます.ローカルメソッドは、複数のローカルメソッドで渡すことができます.たとえば、MyNewStringは、NewObjectが作成したstringリファレンスを返し、simplejni_に渡します.referencesExampleローカルメソッドは、C.referencesExample()を呼び出すjavaレイヤに再び渡されます.次に、NewObjectによって作成されたローカル参照が破壊されます.ローカルリファレンスも作成されたスレッドでのみ有効であり、1つのスレッドで作成されたローカルリファレンスは、別のスレッドでは使用できません.ローカルメソッドでグローバル変数を使用してローカルリファレンスを格納し、他のスレッドにローカルリファレンスを使用することを期待すると、プログラミングのエラーが発生します.
    2.2グローバルリファレンス(Global References)グローバルリファレンスは、DeleteGlobalRef関数をアクティブに呼び出すまで、複数のローカルメソッドで呼び出すことができ、複数のスレッドで交差して使用することができます.そうしないと、有効になります.ローカルリファレンスと同様に、複数のJNI関数で作成できます.たとえば、NewObject、NewLocalRefなどです.グローバルリファレンスは、JNI関数NewGlobalRefのみで作成できます.引き続きMyNewStringの例を使用して、グローバルリファレンスの使用方法を説明します.
    /* This code is OK */
    
    jstring
    MyNewString(JNIEnv *env, jchar *chars, jint len)
    {
        static jclass stringClass = NULL;
        jcharArray elemArr;
        //static jmethodID cid = NULL;
        jmethodID cid = NULL;
        jstring result;
        ALOGI(" MyNewString::chars = %s, len=%d
    ", chars,len); if (stringClass == NULL) { jclass localRefCls =env->FindClass( "java/lang/String"); if (localRefCls == NULL) { return NULL; /* exception thrown */ } /* Create a global reference */ stringClass =(jclass) env->NewGlobalRef(localRefCls); /* The local reference is no longer useful */ env->DeleteLocalRef(localRefCls); /* Is the global reference created successfully? */ if (stringClass == NULL) { return NULL; /* out of memory exception thrown */ } } /* It is wrong to use the cached stringClass here, because it may be invalid. */ /* Note that cid is a static variable */ //if (cid == NULL) { /* Get the method ID for the String constructor */ cid =env->GetMethodID(stringClass,"<init>", "([C)V"); if (cid == NULL) { return NULL; /* exception thrown */ } //} /* Create a char[] that holds the string characters */ elemArr = env->NewCharArray(len); if (elemArr == NULL) { return NULL; /* exception thrown */ } env->SetCharArrayRegion(elemArr, 0, len, chars); for(int i=0;i<len;i++){ ALOGI(" MyNewString::elemArr[%d] = %c
    ", i,*(elemArr+i)); } /* Construct a java.lang.String object */ result = (jstring)env->NewObject(stringClass, cid, elemArr); //ALOGI(" MyNewString::result = %s
    ", result); /* Free local references */ env->DeleteLocalRef(elemArr); //env->DeleteLocalRef(stringClass); return result; }

    以上のコードを使用して、次のコードを再度テストすると、system crashはなくなります.
    ...
    ... = C.referencesExample(); // The first call is  OK.
    ... = C.referencesExample(); // The second call is also OK.
    ...
    

    2.3弱いグローバルリファレンス(Weak Global References)
    弱いグローバルリファレンスはJava 2 SDK release 1.2バージョンで新しく導入され、JNI関数NewWeakGlobalRefを使用してこのリファレンスを作成し、JNI関数DeleteWeakGlobalRefを使用してこのリファレンスを削除し、グローバルリファレンスのようにローカルメソッド間呼び出しと異なるスレッド間で交差して使用しますが、参照されたオブジェクトがゴミ回収されるのを阻止することはできません.ここでは、単純に使用すれば、MyNewStringの例を使用して、弱いグローバルリファレンスの使用を説明します.
    NewWeakGlobalRef「代わりに」
    NewGlobalRefでいいです.弱いグローバルリファレンスを使用してバッファリングjavaを格納.lang.Stringクラス、javaのため、弱いグローバルリファレンスを使用すると、何か問題が発生する心配はありません.lang.Stringはシステムクラスであり、ゴミ回収されません.
    ローカルコードバッファリングされたリファレンスにより、リファレンスされたオブジェクトがゴミ回収されることができます.この場合、弱いグローバルアプリケーションがより役立ちます.例えば、ローカルメソッドmypkgを仮定する.MyCls.fクラスclass mypkgが必要です.MyCls 2バッファ(cache)は、クラスを弱いグローバルリファレンスでバッファリングし、mypkgを許可する.MyCls 2クラスがアンインストールされました.
    void mypkg_MyCls_f(JNIEnv *env, jobject self)
    {
    	static jclass myCls2 = NULL;
    	if (myCls2 == NULL) {
    		jclass myCls2Local =env->FindClass( "mypkg/MyCls2");
    		if (myCls2Local == NULL) {
    			return; /* can\u2019t find class */
    		}
    		myCls2 = env->NewWeakGlobalRef(myCls2Local);
    		if (myCls2 == NULL) {
    			return; /* out of memory */
    		}
    	}
    	//... /* use myCls2 */
    }

    ここでは、MyClsとMyCls 2クラスが同じライフサイクル(例えば同じクラスloadedであってもよい)を有すると仮定し、このような状況を考慮する必要があり、MyCls 2がunloadedおよびその後reloadedされ、MyClsとそのローカルメソッドがmypkg_MyClsは依然として使用されている.バッファリングされた弱いグローバルリファレンスがアクティブなクラスオブジェクトまたはゴミ回収されたクラスオブジェクトを指すかどうかを確認する必要があります.
    2.4 .比較参照(Comparing References)は、2つのローカル参照、グローバル適用、または弱いグローバル参照を与え、JNI関数IsSameObjectを使用して同じオブジェクトを指すかどうかを決定します.env->IsSameObject(obj1, obj2); JNIのNULL参照で、JAVA VMのnullオブジェクトを参照します.objがローカル参照またはグローバル参照を表す場合、objがnullオブジェクトを参照しているかどうかを決定するには、次の式を使用します.
    env->IsSameObject(obj1, NULL);
    
      
    
    obj == NUL

    以上の規則は弱いグローバル参照に対して少し異なっていて、IssSameObjectは弱いグローバル参照に対して特別な用途があって、IssSameObjectを使って1つのnon-NULLの弱いグローバル参照が依然として1つのアクティブなオブジェクトを指しているかどうかを決定することができて、wobjがnon-NULLの弱いグローバル参照であると仮定して、以下のように呼び出します:
    env->IsSameObject(wobj, NULL);

    wobjが回収されたオブジェクトを参照すると、JNI_に戻ります.TRUE、そうでなければ、アクティブなオブジェクトを参照してJNI_を返します.FALSE.
    3.フリーリファレンス(Freeing References)は、被リファレンスオブジェクトがメモリを占有することに加えて、JNIリファレンス自体も一定数のメモリ(memory)を消費し、所定の時間にプログラムがリファレンス数を使用することを考慮する必要があり、特にプログラムの実行中に、任意の時点でローカルリファレンス数の上限を作成することができることを考慮する必要がある.ローカルリファレンスが最終的にVMによって自動的に解放されても.瞬時に過度に参照して作成すると、メモリが消費される可能性があります.3.1ローカルリファレンスの解放(Freeing Local References)多くの場合、ローカルメソッドの実装時にローカルリファレンスの解放の問題を心配する必要はありません.ローカルメソッドが返されると自動的に解放されますが、JNI関数DeleteLocalRefを呼び出して解放する必要があります.
  • ローカルメソッドでは大量のローカルリファレンスを作成する必要があり、内部JNIローカルリファレンステーブルがオーバーフローする可能性があります.これは、使用されなくなったローカルリファレンスを迅速に削除することです.例:
  • for (i = 0; i < len; i++) {
        jstring jstr = env->GetObjectArrayElement(arr, i);
        ... /* process jstr */
        env->DeleteLocalRef(jstr);
        }
  • 未知の名前のコンテキスト呼び出しのためにツール的な関数を書きたい場合は、未使用のローカル参照を迅速に削除する必要があります.
  • ローカルメソッドはまったく返されません.たとえば、ローカルメソッドはイベント割り当てのループであり、ループ内で作成されたローカルリファレンスを解放する必要があり、ローカルリファレンス数の累積によるメモリ漏洩を回避します.
  • ローカルメソッドで大きなオブジェクトにアクセスし、ローカルリファレンスを作成します.ローカルメソッドは、オブジェクトが残りのコードで使用されなくなっても、戻る前に添付ファイルの時間計算を実行します.ローカルリファレンスは、オブジェクトが戻るまでオブジェクトの解放を阻止します.例:
  •  /* A native method implementation */
        void pkg_Cls_func(JNIEnv *env, jobject this)
        {
        lref = ...
        /* a large Java object */
        ...
        /* last use of lref */
        env->DeleteLocalRef(lref);
        lengthyComputation();
        return;
        /* may take some time */
        /* all local refs are freed */
        }

    3.2管理ローカルリファレンス(Managing Local References)Java 2 SDK release 1.2バージョンから、jobject NewLocalRef(jobject ref)jint EnsureLocalCapacity(jint capacity)jint PushLocalFrame(jint capacity)jobject PopLocalFrame(jobject result)JNI仕様で、ローカルリファレンス(Managing Local References)を管理する追加の関数を提供します.1つのVMは、各ローカルメソッドが最大16個のローカルリファレンスを作成できることを自動的に保証します.経験的には、JAVA仮想マシンでは、複雑な相互作用を含まないローカルメソッドのオブジェクトに十分ですが、追加のローカルリファレンスを作成する必要があります.ローカルメソッドはJNI関数EnsureLocalCapacityを呼び出し、十分なローカルリファレンスが有効であることを確認できます.経験により、Java仮想マシン内のオブジェクトに複雑な相互作用を含まないほとんどのネイティブメソッドに十分な能力が提供されることが明らかになりました.
    /* The number of local references to be created is equal to
    the length of the array. */
    if (env->EnsureLocalCapacity(len)) < 0) {
    ... /* out of memory */
    }
    for (i = 0; i < len; i++) {
    jstring jstr = env->GetObjectArrayElement(arr, i);
    ... /* process jstr */
    /* DeleteLocalRef is no longer necessary */
    }

    また、JNI関数push/PopLocalFrameでは、ネストされたローカル参照の範囲を作成できます.例:
    #define N_REFS ... /* the maximum number of local references
    used in each iteration */
    for (i = 0; i < len; i++) {
    if (env->PushLocalFrame(N_REFS) < 0) {
    ... /* out of memory */
    }
    jstr = env->GetObjectArrayElement(arr, i);
    ... /* process jstr */
    env->PopLocalFrame(NULL);
    }

    PushLocalFrameは新しい範囲を作成し、PopLocalFrameはその範囲内のすべてのローカル参照を解放する範囲を破壊します.
    3.3グローバルリファレンスの解放(Freeing Global References)
    ローカルコードでグローバルリファレンスにアクセスする必要がなくなった場合は、DeleteGlobalRefを呼び出して解放する必要があります.失敗したJAVA仮想マシンを呼び出すと、対応するオブジェクトは回収されません.システム内の他の場所でもオブジェクトは使用されません.
    JNI関数DeleteWeakGlobalRefを呼び出すと、使用されなくなった弱いグローバルリファレンスオブジェクトを解放できます.呼び出しに失敗した場合、仮想マシンはリファレンスオブジェクトを回収できますが、新しいアプリケーションから弱いリファレンスで消費されたメモリは使用できません.