JNI表溢出,即使使用deleteLocalRef后仍然存在。

6
当我运行代码时,出现了一个错误 "failed adding to JNI local ref table has 512 entries"。
这是我的代码:
jstring pJNIData = pJNIEnv->NewStringUTF ( variables[0].GetStringValue() );

pJNIEnv->CallStaticVoidMethod ( pJNIActivityClass, pJNIMethodIDStartTime, pJNIData ) ;

pJNIEnv->DeleteLocalRef(pJNIData);

我已经阅读了几篇建议,但是都没有起作用!尽管使用了 DeleteLocalRef,它仍然无法正常工作。该函数在一个分析器中使用,该分析器实际上调用所有函数...


你确定这是填充ref表的代码吗?当您使用一个常量硬编码pJNIData时,问题是否消失? - Pavel Zdenek
@vtmarvin 不确定。这对参考表有影响吗? --> JNIEnv* pJNIEnv = profilerGetJNIEnv(); if(!pJNIEnv) { LOGE("Profiler ERROR: Java环境缺失(null)!"); return; } - shehzad_ucdavis
2个回答

2

我曾经遇到过这种情况,当JNI方法调用Java代码时(在我的情况下,该方法不是静态的)。据我所知,当从JNI调用Java方法时,未使用的局部引用不会自动删除(也就是说,在顶层JNI函数返回之前)。

如果我没记错的话,日志中已经有关于内存对象的信息了,或者我可以添加一些日志;通过这些信息,我发现了之前没有提到的垃圾项。它们是两个数组和一个类,在后续调用中创建,但未被垃圾回收。

// in a function that calls a Java method from JNI
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/something/MyClass");
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B");
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX);

...
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
// no need to do anything with mid

请注意,尽管这三个本地引用是以不同的方式获取的,但它们都存在于当前线程的本地方法中。JNI返回的每个对象都是一个“本地引用”。这意味着它在当前线程的当前本地方法中有效。即使对象本身在本地方法返回后继续存在,引用也无效。这适用于jobject的所有子类,包括jclass和jarray。请注意:方法和字段ID只是32位标识符,不是对象引用,不应传递给NewGlobalRef。GetStringUTFChars和GetByteArrayElements等函数返回的原始数据指针也不是对象。有用链接:http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references (或查找Dalvik VM文档dalvik/docs/jni-tips.html并定位“Local vs. Global References”一节)。

0

我想我应该提供一些帮助,以防其他人遇到这个问题。这是一个奇怪的情况,让我困惑了几个小时!

好的,我有一个 NDK 应用程序,被调用的 Java 代码位于在运行时加载的 apk 中。我不知道运行时加载是否会对此产生影响,但我认为我应该提一下。

现在,在 c++ 方法中,我使用 find class 和 getmethodid 来获取 HashMap 的构造函数,并调用它来获取一个新的 HashMap 实例。然后,我使用 jni 调用从 c++ 端填充 HashMap。到目前为止都很好。

然后,我将 HashMap 传递给 Java 代码,同样,一切都按预期工作。一旦 Java 代码返回,我调用 DeleteLocalRef 删除 HashMap。没有抛出错误,但引用未被删除。

只有当我最终运行超过 512 个本地引用(从多次调用此函数)时才出现这个问题,错误转储显示本地引用存储中的最后 10 个项目几乎都是 HashMaps。我可以理解 GC 不会在方法结束时收集这些引用,因为我正在制作一个多线程 ndk 应用程序。但是,DeleteLocalRef 应该起作用。

解决方法: 最后我发现通过一个jni调用我编写的java方法创建HashMap是可行的,并且引用可以释放。似乎很疯狂,只是返回一个新的HashMap的java函数,但它确实有效,所以暂时我接受了这个方案 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接