14

I have the following problem, from c++ I send huge string[] to java. huge = at most 20 rows; I am doing the following

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

this is happening in a loop so I am doing it for every object I want to save in the database, so as you can imagine, it happends lots of times.

So I am considerate of the VM and delete the local refs of every string I create, but still I get :

 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

Any ideas as to why the overflow is happening? what am I doing wrong?

bobby
  • 2,629
  • 5
  • 30
  • 56
Tancho
  • 1,581
  • 3
  • 22
  • 40
  • I am quite convinced that you need to call `ReleaseStringUTFChars` for each `NewStringUTF`, regardless of whether you do `DeleteLocalRef` afterwards. – Pavel Zdenek Jun 18 '12 at 10:17
  • Does it make sense? since I am not accessing a jstring from the vm but sending one to it... Would I have to create a pointer to the string and then call use that pointer to do the releaseStringUtfChars.. – Tancho Jun 18 '12 at 10:21
  • 2
    You only need to call `ReleaseStringUTFChars()` if you previously called `GetStringUTFChars()` to create a C-String from a Java-String. That isn't the case here. – David Wasser Jun 18 '12 at 11:32

2 Answers2

25

Try to delete the local refs immediately after use. Like this:

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

I'm not sure if GetObjectArrayElement() is returning the same localref or creating a new one. If it is creating a new one then that would explain why you are filling up the localref table.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • I don't see how the system can get a new reference? you think that by getElementObject they return a copy of the pointer? not the original one? – Tancho Jun 18 '12 at 11:50
  • Yes, it returns a copy of the pointer (actually it isn't just a pointer, it is a localref). See @jogabonito's answer for more details. – David Wasser Jun 18 '12 at 13:33
3

@Davids's answer is correct. You are running out of space in the local reference table since the references to the temporary strings you are creating using NewStringUTF are getting lost. If you look at your error message you can see

392 of Ljava/lang/String; 28B (392 unique)

The references NewStringUTF objects you are creating are "anonymous" and will be lost. By deleting the reference to data you are only deleting the reference to the array. The strings will still be in memory till the function exits. I also feel that passing emptyString as an argument to NewObjectArray is superflous and NULL will do as well

bobby
  • 2,629
  • 5
  • 30
  • 56