2016-03-21 5 views
-1

私はC++で実装されたネイティブ関数を持つJavaクラスを持っています。これはnz.ac.unitec.BigIntegerと呼ばれています。 nz.ac.unitec.BigIntegerのネイティブ実装は単純で、java.math.BigIntegerをラップし、そのコンストラクタとadd,subtractmultiply ...関数を呼び出すだけです。フィールドmNativeContextnz.ac.unitec.BigIntegerは、java.math.BigIntegerオブジェクトへのグローバル参照を格納するために使用されます。オブジェクトにはファイナライザがあり、オブジェクトがガベージコレクトされたときにグローバル参照を破棄して、グローバル参照が漏れていないようにします。JNIネイティブ関数で作成されたグローバル参照を解放する必要があるのはなぜですか?

単純なテストループを実行して、作成されたオブジェクトを明示的に解放せずにnz.ac.unitec.BigIntegerオブジェクトを多数作成したとき、JNIエラーが次のログ(LOG1)として報告されました。しかし、テスト関数を離れる前に、作成したオブジェクトを明示的に解放すると、ストレステストは正常に実行できます。

オブジェクトがガベージコレクションされたときにファイナライザを削除すると、JVMのグローバル参照が不足するのはなぜですか?

LOG1:

F/art  (10730): art/runtime/indirect_reference_table.cc:113] JNI ERROR (app bug): global reference table overflow (max=51200) 

F/art  (10730): art/runtime/indirect_reference_table.cc:113] global reference table dump: 
F/art  (10730): art/runtime/indirect_reference_table.cc:113] Last 10 entries (of 51200): 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51199: 0x12e88790 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51198: 0x12e85490 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51197: 0x12e81790 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51196: 0x12e7e760 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51195: 0x12e7ab20 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51194: 0x12e77790 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51193: 0x12e73a90 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51192: 0x12e71af0 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51191: 0x12e6dd60 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  51190: 0x12e6b9a0 java.math.BigInteger 
F/art  (10730): art/runtime/indirect_reference_table.cc:113] Summary: 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  1456 of java.math.BigInteger (1456 unique instances) 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]   2 of android.opengl.EGLDisplay (2 unique instances) 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  1889 of java.math.BigInteger (1889 unique instances) 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]   1 of java.lang.String 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  27 of java.math.BigInteger (27 unique instances) 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]   1 of java.lang.String 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]  3771 of java.math.BigInteger (3771 unique instances) 
F/art  (10730): art/runtime/indirect_reference_table.cc:113]   1 of dalvik.system.PathClassLoader 
F/art  (10730): art/runtime/runtime.cc:284] Runtime aborting... 
F/art  (10730): art/runtime/runtime.cc:284] Aborting thread: 

Calculator.java

package nz.ac.unitec.calculator; 

public class MainActivity extends AppCompatActivity 
{ 
    protected void onCreate(Bundle savedInstanceState) 
    { 
     Random ra = new Random(); 
     for(int i=0; i<6000000; ++i) { 
      testCreateFinalize(ra); 
      int m = ra.nextInt(); 
      int n = 8; 
      int re = m + n; 
      Log.i("MainActivity", "re=" + re); 
      //BigInteger result = l.subtract(r); 
     } 

    private void testCreateFinalize(Random ra) 
    { 
     BigInteger l = new BigInteger("100", 10); 

     BigInteger r = new BigInteger("200", 10); 
     //l.release();  when adding this two code lines, the test is ok 
     //r.release();; 
    } 
} 

BigInteger.java

package nz.ac.unitec.mathutils; 
public class BigInteger 
{ 
    static { 
     System.loadLibrary("BigInteger_jni"); 
     native_init(); 
    } 

    private long mNativeContext; 

    private BigInteger() 
    { 
     mNativeContext = 0; 
     native_setup(); 
    } 

    public BigInteger(String val, int radix) 
    { 
     mNativeContext = 0; 
     native_setup_bystring(val, radix); 
    } 

    public void release() { 
     native_finalize(); 
    } 

    protected void finalize() { 
     native_finalize(); 
    } 


    private static native final void native_init(); 
    private native final void native_setup(); 
    private native final void native_setup_bystring(String val, int radix); 
    private native final void native_finalize(); 
    public native String toString(int radix); 


    public native BigInteger add(BigInteger rval); 
    public native BigInteger multiply(BigInteger rval); 
    public native BigInteger subtract(BigInteger rval); 

} 

ネイティブコード

static jobject getNativeBigInteger_l(JNIEnv* env, jobject thiz) 
{ 
    return reinterpret_cast<jobject> (env->GetLongField(thiz, fields.context)); //reinterpret_cast<jobject> 
} 

static void setNativeBigInteger_l(JNIEnv* env, jobject thiz, jobject bi) 
{ 
    env->SetLongField(thiz, fields.context, reinterpret_cast<jlong>(bi)); //reinterpret_cast<jlong> 
} 

JNIEXPORT void JNICALL Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring 
     (JNIEnv *env, jobject thiz, jstring val, jint radix) 
{ 

    jclass cls = env->FindClass(gBuiltinClassBigInteger); 
    ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->FindClass(%s) return (%0x)", 
      gBuiltinClassBigInteger, cls); 

    if (cls == NULL) { 
     ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->FindClass(%s) return NULL", gBuiltinClassBigInteger); 
     return; 
    } 

    jmethodID constructor = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;I)V"); 
    if (constructor == NULL) { 
     env->DeleteLocalRef(cls); 
     ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->GetMethodID(%s) return NULL", "<init>"); 
     return; 
    } 

    jobject jobj = env->NewObject(cls, constructor, val, radix); 
    ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->NewObject return (%0x)", 
      jobj); 
    if (NULL == jobj) { 
     env->DeleteLocalRef(cls); 
     ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->NewObject return NULL"); 
     return; 
    } 

    jobject gjobj = env->NewGlobalRef(jobj); 
    ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env->NewGlobalRef return (%0x)", gjobj); 
    setNativeBigInteger_l(env, thiz, gjobj); 
    env->DeleteLocalRef(jobj); 
    //env->DeleteLocalRef(cls); 
} 

JNIEXPORT void JNICALL 
Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize 
     (JNIEnv *env, jobject thiz) 
{ 
    ALOGV("+native_finalize"); 
    jobject obj = getNativeBigInteger_l(env, thiz); 
    ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize getNativeBigInteger_l return (%0x)", 
          obj); 
    if (obj == NULL) { 
     ALOGE("-native_finalize getNativeBigInteger_l NULL"); 
     return; 
    } 
    env->DeleteGlobalRef(obj); 
    setNativeBigInteger_l(env, thiz, NULL); 
    ALOGV("-native_finalize"); 
} 


static JNINativeMethod gMethods[] = { 
     {"native_init",   "()V", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1init}, 

     {"native_setup", "()V", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1setup}, 

     {"native_setup_bystring", "(Ljava/lang/String;I)V", 
       (void*)Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring}, 


     {"native_finalize",  "()V", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize}, 

     {"add",  "(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;", 
     (void *)Java_nz_ac_unitec_mathutils_BigInteger_add}, 

     {"multiply","(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_multiply}, 
     {"subtract","(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_subtract}, 
     {"toString","(I)Ljava/lang/String;", 
       (void *)Java_nz_ac_unitec_mathutils_BigInteger_toString} 
}; 

int register_Java_nz_ac_unitec_mathutils_BigInteger(JNIEnv *env) 
{ 
    return jniRegisterNativeMethods(env, gClassBigInteger, gMethods, NELEM(gMethods)); 
} 
+0

あなたは数百万の 'BigInteger'オブジェクトを作成していますので、もちろん数百万の' GlobalRefs'を生成します。これはトリックの質問ですか? – EJP

+0

testCreateFinalizeが定期的に返されるため、testCreateFinalizeで作成されたすべてのローカル参照が自動的に解放されるため、nz.ac.mathutils.BigInteger.finalize()が定期的に呼び出されることがあります。ローカル参照(nz.ac.mathutils.BigInteger)が解放されると、nz.ac.mathutils.BigInteger.finalize関数が自動的に呼び出されます。次に、native_finalize JNI関数では、グローバル参照はenv-> DeleteGlobalRef(obj);によって解放されます。この種の実装は、AOSPのJNIオブジェクトandroid.media.MediaScannerとまったく同じです。 –

+0

あなたの質問のように聞こえるのは、*なぜ* JVMグローバルリソーステーブルが限られているのか、なぜこの場合はエラーが表示されるのですか?おそらく、あなたの問題に技術的な解決策を求めるのではなく、そのようにあなたの質問を語るべきでしょう。 –

答えて

2

メモリプールに戻されていないオブジェクトへのグローバル参照が多すぎるため、コードが失敗しています。グローバル参照テーブルには、メモリリークをキャッチしてプログラムのメモリ不足を防ぐのに役立つ最大サイズがあります。貼り付けたログメッセージには、これらのオブジェクトの内容が表示されます。java.math.BigInteger

あなたがnative_setup_bystringの実装を見れば、あなたは新しいBigIntegerオブジェクトへのグローバル参照を作成していることがわかります。

jobject gjobj = env->NewGlobalRef(jobs);

Global references are not automatically garbage collectedはそうあなたが明示的に削除する必要があり、何がありますあなたはあなたのテストで見つけました。

ヒントへのダイレクトメモリ参照(long mNativeContextフィールド)を使用して、内部オブジェクトへの参照を保存するという問題があります。このアプローチは、BigIntegerのガベージコレクションをJVMが管理できないようにしているため、良い方法ではありません。より良いアプローチは、グローバル参照をまったく使用しないで、longの代わりにオブジェクト参照を格納することです。これを行うと、JVMは割り当てているすべてのオブジェクトを自動的に収集することができます。

+0

testCreateFinalizeが定期的に返されるため、testCreateFinalizeで作成されたすべてのローカル参照が自動的に解放されるため、nz.ac.mathutils.BigInteger.finalize()が定期的に呼び出されることがあります。ローカル参照(nz.ac.mathutils.BigInteger)が解放されると、nz.ac.mathutils.BigInteger.finalize関数が自動的に呼び出されます。次に、native_finalize JNI関数では、グローバル参照はenv-> DeleteGlobalRef(obj);によって解放されます。この種の実装は、AOSPのJNIオブジェクトandroid.media.MediaScannerとまったく同じです。 –

+0

これは当てはまりますが、ガベージコレクションはグローバル参照消耗によって引き起こされることはないので、JVMが意図的にこのリソースを再利用する方法はないため、このエラーが表示されます。グローバル参照は、このように使用することを意図していません。 MediaScannerは、あまりにも頻繁に割り当てられないことを保証する不十分なOSリソース(ファイルハンドル)にすでに結び付けられているため、この戦略を使用できます。これは、あなたが参照できるJVMオブジェクトを持っているときには良い戦略ではありません。 –

+0

DeleteGlobalRefは、nz.ac.mathutils.BigIntegerクラスのローカル参照の "finalize"関数のコンテキストで呼び出されるため、JVMグローバルリソーステーブルは決して制限に達しないと思います。だから、私の質問はそれが限界に達した理由です。 –

関連する問題