Jeet
Jeet

Reputation: 1046

Java JNI Programming : Actual usage of the Global References

In my previous question i cached the JNIEnv* between JNI Calls. And from the comment i came to know its invalid and this result me to learn the JNI Local and Global references. I did some test program to understand it. From the test program i am not able to understand the use of the Global references. Because the Local references itself working fine between multiple JNI calls. I have 3 questions from the test program

  1. Eager to know the reason, that how the local reference getting cached and working properly.
  2. Change in the ArrayList size is working and not the String object
  3. Reason to know how the cached JNIEnv in working ,although its invalid (in my previous question).

The test code given below.

jni code :

jclass cls1, cls2, cls3;
jmethodID mth1, mth2, mth3;
jstring str1, str2;
jobject obj1, obj2, obj3;

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnFindClass
(JNIEnv *env, jobject obj) {
    if (cls1 == NULL || str1 == NULL || obj1 == NULL) {
        cout << "Initializing new string object" << endl;
        cls1 = env->FindClass("java/lang/String");
        mth1 = env->GetMethodID(cls1, "<init>", "(Ljava/lang/String;)V");
        str1 = env->NewStringUTF("Apple");
        obj1 = env->NewObject(cls1, mth1, str1);

        mth1 = env->GetMethodID(cls1, "length", "()I");
        long i = (long) env->CallIntMethod(obj1, mth1);
        cout << "Length = " << i << endl;
        //env->DeleteLocalRef(cls1);
        //env->DeleteLocalRef(str1);
        //env->DeleteLocalRef(obj1);
    } else {
        cout << "String Object already initialized" << endl;
        mth1 = env->GetMethodID(cls1, "length", "()I");
        long i = (long) env->CallIntMethod(obj1, mth1);
        cout << "Length = " << i << endl;
    }
}

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_lang_String_2
(JNIEnv *env, jobject obj, jstring str) {
    if (cls2 == NULL || obj2 == NULL) {
        cout << "Initializing from existing string object" << endl;
        cls2 = env->GetObjectClass(str);
        obj2 = (jobject) env->NewLocalRef(str);
        mth2 = env->GetMethodID(cls2, "length", "()I");
        long i = (long) env->CallIntMethod(obj2, mth2);
        cout << "Length = " << i << endl;
        //env->DeleteLocalRef(cls2);
        //env->DeleteLocalRef(obj3);
    } else {
        cout << "Object already initialized" << endl;
        mth2 = env->GetMethodID(cls2, "length", "()I");
        long i = (long) env->CallIntMethod(obj2, mth2);
        cout << "Length = " << i << endl;
    }
}

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_util_ArrayList_2
(JNIEnv *env, jobject obj, jobject lst) {
if (cls3 == NULL || obj3 == NULL) {
        cout << "Initializing from existing ArrayList object" << endl;
        cls3 = env->GetObjectClass(lst);
        obj3 = (jobject) env->NewLocalRef(lst);
        mth3 = env->GetMethodID(cls3, "size", "()I");
        long i = (long) env->CallIntMethod(obj3, mth3);
        cout << "Size = " << i << endl;
        //env->DeleteLocalRef(cls3);
        //env->DeleteLocalRef(obj3);
    } else {
        cout << "Object already initialized" << endl;
        mth3 = env->GetMethodID(cls3, "size", "()I");
        long i = (long) env->CallIntMethod(obj3, mth3);
        cout << "Length = " << i << endl;
    }
}

Calling Java code (Test Code) :

a.fnFindClass();
a.fnFindClass();

String str = new String("Android OS");
a.fnGetExistingObject(str);
//Increasing the size
str = new String("Will modified string length get effect");
a.fnGetExistingObject(str);

ArrayList<Integer> al = new ArrayList<>();
al.add(1);al.add(2);al.add(3);
a.fnGetExistingObject(al);
al.add(4);al.add(5); //Increasing the size
a.fnGetExistingObject(al);

Test Result :

Initializing new string object
Length = 5
String Object already initialized
Length = 5

Initializing from existing string object
Length = 10
Object already initialized
Length = 10

Initializing from existing ArrayList object
Size = 3
Object already initialized
Length = 5

Thanks In Advance.

Upvotes: 1

Views: 208

Answers (1)

WillShackleford
WillShackleford

Reputation: 7008

Global references prevent the garbage collector from deleting the relevant object. The object might also not be collected just because another java object has a reference to it or because the garbage collector didn't run in a short period between calls. You shouldn't rely on this and instead keep global references to anything you might need later.

ArrayLists can be resized. Strings are immutable and one always needs to create a new string for functions like substring or append.

I believe the issue with keeping JNIEnv * has to do with thread safety. There is no way for the C++ code to know two different calls don't come from two different threads. Each env needs to be attached to a different thread.

Upvotes: 1

Related Questions