为什么我需要释放在JNI本机函数中创建的全局引用?

我有一个Java类,其中包含用C ++实现的本机函数,称为nz.ac.unitec.BigIntegernz.ac.unitec.BigInteger本机实现很简单,只需包装java.math.BigInteger并调用其构造函数并addsubtractmultiply …函数。 mNativeContext中的字段mNativeContext用于存储对java.math.BigInteger对象的全局引用。 该对象有一个终结器,当对象被垃圾收集时应该销毁全局引用,所以我没有泄漏全局引用。

当我运行一个简单的测试循环来创建许多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 (env->GetLongField(thiz, fields.context)); //reinterpret_cast } static void setNativeBigInteger_l(JNIEnv* env, jobject thiz, jobject bi) { env->SetLongField(thiz, fields.context, reinterpret_cast(bi)); //reinterpret_cast } 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, "", "(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", ""); 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)); } 

您的代码失败,因为您对尚未释放回内存池的对象有太多全局引用。 全局引用表具有最大大小,可帮助您捕获内存泄漏并防止程序内存不足。 粘贴的日志消息告诉您这些对象是什么: java.math.BigInteger

如果查看native_setup_bystring的实现,可以看到您正在创建对新BigInteger对象的全局引用:

jobject gjobj = env->NewGlobalRef(jobs);

全局引用不会自动进行垃圾回收,因此您需要明确删除它们,这是您在测试中发现的内容。

您的方法的问题是您使用直接内存引用存储对内部对象的引用(您的long mNativeContext字段)。 这种方法不是很好,因为您阻止JVM管理BigIntegers的垃圾收集。 更好的方法是避免使用全局引用,并存储对象引用而不是long 。 如果这样做,那么JVM将能够自动收集您正在分配的所有对象。