2016-03-21 79 views
-1

我有一个在C++中实现本机函数的Java类,它被称为nz.ac.unitec.BigIntegernz.ac.unitec.BigInteger的本地实现很简单,只是包装java.math.BigInteger并调用其构造函数和add,subtract, multiply ...函数。 nz.ac.unitec.BigInteger中的字段mNativeContext用于存储对java.math.BigInteger对象的全局引用。该对象有一个终结器,当对象被垃圾收集时应该销毁全局引用,所以我没有泄漏全局引用。为什么我需要释放在JNI本机函数中创建的全局引用?

当我运行一个简单的测试循环来创建一个nz.ac.unitec.BigInteger对象的数量而没有显式释放创建的对象时,JNI错误报告如下log(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

问题是,nz.ac.mathutils.BigInteger.finalize()将被定期调用,因为testCreateFinalize将定期返回,所有在testCreateFinalize中创建的本地引用将被自动释放。当本地引用(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字段)。这种方法并不好,因为您正在阻止JVM管理BigIntegers的垃圾回收。更好的方法是避免使用全局引用,并存储对象引用而不是long。如果你这样做,那么JVM将能够自动收集你分配的所有对象。

+0

问题是,nz.ac.mathutils.BigInteger.finalize()将被定期调用,因为testCreateFinalize将定期返回,所有在testCreateFinalize中创建的本地引用将被自动释放。当本地引用(nz.ac.mathutils.BigInteger)被释放时,nz.ac.mathutils.BigInteger.finalize函数将被自动调用。然后在native_finalize JNI函数中,全局引用将通过env-> DeleteGlobalRef(obj);发布。这种实现就像AOSP中的JNI对象android.media.MediaScanner一样。 –

+0

虽然这是真的,但垃圾收集不会被全局引用耗尽触发,因此JVM无法有意收回此资源,这就是您看到此错误的原因。全局引用并不打算用于这种方式。 MediaScanner可以使用这种策略,因为它已经绑定到一个稀缺的操作系统资源(文件句柄),以确保它不会被频繁分配。当你有一个你可以参考的JVM对象时,这不是一个好策略。 –

+0

DeleteGlobalRef将在nz.ac.mathutils.BigInteger类的本地引用的“finalize”函数的上下文中调用,所以我认为JVM全局资源表永远不会达到极限。所以,我的问题是为什么它达到了极限。 –

相关问题