为什么我需要释放在 JNI 原生函数中创建的全局引用? [英] Why do I need to release global references created in JNI native functions?

查看:15
本文介绍了为什么我需要释放在 JNI 原生函数中创建的全局引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用 C++ 实现的本机函数的 Java 类,称为 nz.ac.unitec.BigInteger.nz.ac.unitec.BigInteger 的原生实现很简单,只需将java.math.BigInteger 包装起来,并调用其构造函数和addsubtract, multiply ... 函数.nz.ac.unitec.BigInteger 中的字段 mNativeContext 用于存储对 java.math.BigInteger 对象的全局引用.该对象有一个终结器,它应该在对象被垃圾回收时销毁全局引用,这样我就不会泄漏全局引用.

I have a Java class with native functions implemented in C++ which is called nz.ac.unitec.BigInteger. The native implementation of nz.ac.unitec.BigInteger is simple, just wraps the java.math.BigInteger and call its constructor and add, subtract, multiply ... functions. A field mNativeContext in nz.ac.unitec.BigInteger is used to store a global reference to a java.math.BigInteger object. The object has a finalizer that should destroy the global reference when the object is garbage collected so I am not leaking global references.

当我运行一个简单的测试循环来创建多个 nz.ac.unitec.BigInteger 对象而没有明确释放创建的对象时,JNI 错误被报告为以下日志 (LOG1).但是如果我在离开测试函数之前显式释放创建的对象,压力测试可以成功执行.

When I runned a simple test loop to create a number of nz.ac.unitec.BigInteger objects without explicitly releasing the created objects, a JNI error was reported as following log (LOG1). But if I release the created objects explicitly before leaving the test function, the stress test can be executed successfully.

当我有一个终结器在对象被垃圾回收时删除全局引用,为什么 JVM 用完了全局引用?

Why is the JVM running out of global references when I have a finalizer to delete them when the objects are garbage collected?

日志1:

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:

计算器.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

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));
}

推荐答案

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

Your code is failing because you have too many global references to objects that have not been released back into the memory pool. The global reference table has a maximum size to help you catch memory leaks and prevent your program from running out of memory. The log message you pasted tells you what those objects are: java.math.BigInteger.

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

If you look at your implementation of native_setup_bystring you can see that you're creating a global reference to a new BigInteger object:

jobject gjobj = env->NewGlobalRef(jobs);

全局引用不会自动垃圾收集[1][2] 所以你需要明确删除它们,这是你在测试中发现的.

Global references are not automatically garbage collected [1][2] so you need to explicitly delete them, which is what you've found in your testing.

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

The problem with your approach is that you're storing a reference to the internal object using a direct memory reference into the heap (your long mNativeContext field). This approach is not a good one because you're preventing the the JVM from managing the garbage collection of your BigIntegers. A better approach would be to avoid using a global reference at all, and store an object reference instead of a long. If you do this then the JVM will be able to automatically collect all of the objects you're allocating.

这篇关于为什么我需要释放在 JNI 原生函数中创建的全局引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆