Android NDK超出了Dalvik JNI本地引用表

14

我有一个问题,我想从C++向Java发送大型字符串数组string[]。其中“大型”指的是最多20个元素;我正在尝试以下操作:

jint jtype = 2;
jstring emptyString = env->NewStringUTF("");
jobjectArray data = (jobjectArray)env->NewObjectArray(7, env->FindClass("java/lang/String"), emptyString);

env->SetObjectArrayElement( data,0,env->NewStringUTF(item->get_id().c_str());
env->SetObjectArrayElement( data,1,env->NewStringUTF(item->get_number().c_str());
env->SetObjectArrayElement( data,2,env->NewStringUTF(item->get_fullname().c_str());
env->SetObjectArrayElement( data,3,env->NewStringUTF(item->get_mf().c_str());
env->SetObjectArrayElement( data,4,env->NewStringUTF(item->get_dob().c_str());
env->CallVoidMethod(dao, jsaveItem, data, jtype);
int i;
for (i = 0; i < 5; ++i) {
   jstring string = (jstring) env->GetObjectArrayElement(data, i);
   env->DeleteLocalRef(string);
}
env->DeleteLocalRef(emptyString);
env->DeleteLocalRef(data);
env->DeleteLocalRef(dao);

这是在循环中发生的,所以我需要为要保存到数据库中的每个对象都执行此操作,因此,正如您可以想象的那样,它会发生很多次。

所以我考虑了虚拟机并删除了我创建的每个字符串的本地引用,但仍然出现以下问题:

 ReferenceTable overflow (max=512)
Last 10 entries in JNI local reference table:
  502: 0x40552880 cls=Ljava/lang/String; (28 bytes)
  503: 0x405528b8 cls=Ljava/lang/String; (28 bytes)
  504: 0x4051f8d0 cls=Ljava/lang/Class; 'Lcom/project/storage/userdata/DataDao;' (212 bytes)
  505: 0x4052eb38 cls=Lcom/project/storage/userdata/DataDao; (12 bytes)
  506: 0x4051f8d0 cls=Ljava/lang/Class; 'Lcom/project/storage/userdata/DataDao;' (212 bytes)
  507: 0x4052eb38 cls=Lcom/project/storage/userdata/DataDao; (12 bytes)
  508: 0x4051f8d0 cls=Ljava/lang/Class; 'Lcom/project/storage/userdata/DataDao;' (212 bytes)
  509: 0x4052eb38 cls=Lcom/project/storage/userdata/DataDao; (12 bytes)
  510: 0x4051f8d0 cls=Ljava/lang/Class; 'Lcom/project/storage/userdata/DataDao;' (212 bytes)
  511: 0x4052eb38 cls=Lcom/project/storage/userdata/DataDao; (12 bytes)
JNI local reference table summary (512 entries):
   58 of Ljava/lang/Class; 212B (1 unique)
    1 of Ljava/lang/Class; 236B
   25 of Ljava/lang/Class; 284B (1 unique)
    1 of Ljava/lang/Class; 572B
  392 of Ljava/lang/String; 28B (392 unique)
    1 of Ljava/lang/String; 36B
    1 of [Ljava/lang/String; 28B
    2 of [Ljava/lang/String; 92B (2 unique)
   31 of Lcom/project/storage/userdata/DataDao; 12B (1 unique)
Memory held directly by tracked refs is 12540 bytes

有什么想法,为什么会发生溢出?我做错了什么吗?


我非常确信,无论您是否在之后执行DeleteLocalRef,每个NewStringUTF都需要调用ReleaseStringUTFChars - Pavel Zdenek
这有意义吗?因为我没有从VM访问jstring,而是将其发送到VM...我是否需要创建一个指向该字符串的指针,然后调用使用该指针进行releaseStringUtfChars。 - Tancho
2
只有在之前调用GetStringUTFChars()从Java-String创建C-String后,才需要调用ReleaseStringUTFChars()。这里不是这种情况。 - David Wasser
2个回答

25

尝试在使用后立即删除本地引用。像这样:

jstring string;
string = env->NewStringUTF(item->get_id().c_str());
env->SetObjectArrayElement( data,0,string);
env->DeleteLocalRef(string);
string = env->NewStringUTF(item->get_number().c_str());
env->SetObjectArrayElement( data,1,string);
env->DeleteLocalRef(string);
string = env->NewStringUTF(item->get_fullname().c_str());
env->SetObjectArrayElement( data,2,string);
env->DeleteLocalRef(string);
string = env->NewStringUTF(item->get_mf().c_str());
env->SetObjectArrayElement( data,3,string);
env->DeleteLocalRef(string);
string = env->NewStringUTF(item->get_dob().c_str());
env->SetObjectArrayElement( data,4,string);
env->DeleteLocalRef(string);
env->CallVoidMethod(dao, jsaveItem, data, jtype);

我不确定GetObjectArrayElement()是否返回相同的本地引用或创建新的本地引用。如果它正在创建一个新的本地引用,那么这就可以解释为什么你正在填充本地引用表。


我不明白系统如何获得新的引用?你认为通过getElementObject它们返回指针的副本?而不是原始的指针? - Tancho
是的,它返回指针的副本(实际上不仅仅是指针,它是一个局部引用)。有关更多详细信息,请参阅@jogabonito的答案。 - David Wasser

3
@Davids的答案是正确的。由于使用NewStringUTF创建的临时字符串的引用丢失,您正在本地引用表中耗尽空间。如果查看错误消息,您会看到
392 of Ljava/lang/String; 28B(392个唯一)
您正在创建的NewStringUTF对象是“匿名的”,将被丢失。通过删除对data的引用,您仅删除了对数组的引用。这些字符串仍将在内存中,直到函数退出。我还觉得将emptyString作为NewObjectArray的参数传递是多余的,NULL也可以。

好的,我今天会尝试并分享结果! - Tancho

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