为什么我得到本地参考表溢出?

时间:2015-11-30 18:35:28

标签: android-ndk java-native-interface

我正在试图找出当提示点数量很高时如何解决代码崩溃问题。我的测试每次都会使用530个提示项重现它。每个提示的nsmallest为1.

您可以在下面看到我的代码。基本上dataSize是从java层调用的。这会触发从jni层多次调用java层。不知怎的,这导致表溢出。我在想,每次调用静态java函数都不会释放分配的字符串值导致堆栈溢出?

示例代码

native_update_cue_points

Stacktrace

static void native_update_cue_points(JNIEnv *env, jclass clazz)
{
    if (smpMediaPlayer)
    {
        std::list<Cue> cuePoints;

        smpMediaPlayer->getCuePoints(&cuePoints);

        for (std::list<Cue>::iterator it = cuePoints.begin(); it != cuePoints.end(); it++)
        {
            Cue cue = *it;

            java_update_cue_point(env, clazz, smFields.global_ref_thiz, 0, &cue);
        }
    }
}

static void java_update_cue_point (JNIEnv *env, jclass clazz, jobject thiz, int reqType, Cue *pCue)
{
    if (!pCue) {
        return;
    }

    jlong id = pCue->id;
    jint type = pCue->type;
    jint extra = pCue->extra;
    jlong pos = pCue->pos;
    jlong duration = pCue->duration;
    jobjectArray jkeys = NULL;
    jobjectArray jvalues = NULL;

    int dataSize = pCue->data.size();

    if (0 < dataSize)
    {
        jkeys = env->NewObjectArray(dataSize, env->FindClass("java/lang/String"), NULL);
        jvalues = env->NewObjectArray(dataSize, env->FindClass("[B"), NULL);

        int position = 0;
        for (std::map<std::string, std::string>::iterator it = pCue->data.begin(); it != pCue->data.end(); it++)
        {
            // set key
            env->SetObjectArrayElement(jkeys, position, env->NewStringUTF(it->first.c_str()));

            // set value
            jbyteArray byteArray = env->NewByteArray(it->second.length());
            env->SetByteArrayRegion(byteArray, 0, it->second.length(), (const signed char *)it->second.c_str());
            env->SetObjectArrayElement(jvalues, position, byteArray);
            env->DeleteLocalRef(byteArray);

            position++;
        }
    }

    // report update
    env->CallStaticVoidMethod(
                clazz,
                smFields.native_callback_add_cue_point,
                thiz,
                (jint)reqType, id, type, extra, pos, duration, jkeys, jvalues);
}

更新1:
我做了一些挖掘,并且能够将其缩小到调用FindClass。这让我更加困惑! :/这是我简化的JNI ERROR (app bug): local reference table overflow (max=512) local reference table dump: Last 10 entries (of 512): 511: 0x13054090 java.lang.String[] (1 elements) 510: 0x70732980 java.lang.Class<java.lang.String> 509: 0x130517c0 java.lang.String "song_artist" 508: 0x13054080 byte[][] (1 elements) 507: 0x7079af18 java.lang.Class<byte[]> 506: 0x13054070 java.lang.String[] (1 elements) 505: 0x70732980 java.lang.Class<java.lang.String> 504: 0x13051700 java.lang.String "song_artist" 503: 0x12d17ff0 byte[][] (1 elements) 502: 0x7079af18 java.lang.Class<byte[]> Summary: 205 of java.lang.Class (2 unique instances) 103 of java.lang.String[] (1 elements) (103 unique instances) 102 of java.lang.String (102 unique instances) 102 of byte[][] (1 elements) (102 unique instances) 方法。我是否需要以某种方式释放FindClass调用!?

代码

native_update_cue_point

Stacktrace

static void java_update_cue_point (JNIEnv *env, jclass clazz, jobject thiz, int reqType, Cue *pCue)
{
    jclass jstringClass = env->FindClass("java/lang/String");
}

更新2:
我能够通过在每个本地对象上执行JNI ERROR (app bug): local reference table overflow (max=512) local reference table dump: Last 10 entries (of 512): 511: 0x70732980 java.lang.Class<java.lang.String> 510: 0x70732980 java.lang.Class<java.lang.String> 509: 0x70732980 java.lang.Class<java.lang.String> 508: 0x70732980 java.lang.Class<java.lang.String> 507: 0x70732980 java.lang.Class<java.lang.String> 506: 0x70732980 java.lang.Class<java.lang.String> 505: 0x70732980 java.lang.Class<java.lang.String> 504: 0x70732980 java.lang.Class<java.lang.String> 503: 0x70732980 java.lang.Class<java.lang.String> 502: 0x70732980 java.lang.Class<java.lang.String> Summary: 512 of java.lang.Class (1 unique instances) 来修复表溢出,无论java VM是否会处理它。我的想法是VM正在累积堆栈中的项目,这些项目将在执行后释放。我需要从堆栈中显式删除每个项目,以避免在大型循环中稍后删除不需要的累积。

我仍然觉得我还没有完全理解为什么会这样,以及为什么我的解决方案会解决它。

固定代码

DeleteLocalRef

2 个答案:

答案 0 :(得分:3)

你创建了太多的本地引用(两个用于类,两个用于对象数组,还有一些用于字符串和字节数组每次运行java_update_cue_point)。 VM只能处理有限数量的本地引用。有关文档,请参阅“Referencing Java Objects”。 JNI函数文档中的“Global and Local References”部分也有一些细节。当本机方法返回时,VM将自动释放本地引用,但在像您这样的循环中,您将不得不删除DeleteLocalRef不再需要的引用。

为了优化一点,我的建议是在FindClass的循环之外进行两次native_update_cue_points调用,并将它们作为参数传递给java_update_cue_point。这样,当native_update_cue_points返回时,您将只为VM对象释放的类对象提供两个引用,并且通过不反复查找相同的类来节省处理时间。

答案 1 :(得分:2)

我怀疑你需要在这行代码中创建的Java DeleteLocalRef()对象上调用String

        env->SetObjectArrayElement(jkeys, position, env->NewStringUTF(it->first.c_str()));

您对NewStringUTF()的调用会创建一个带有本地引用的Java对象,您不会将其删除。