JNI references to Java objects

Keywords: Back-end JNI

reference:

JNI learning notes - local and global references

         JNI exposes the class instances and array types of the Java layer as opaque references. The code of the native layer will access this opaque reference through the functions provided by JNI. There are different kinds of references in JNI, including local reference, global reference and weak global application.

        JNI's local references to Java layer instances can be automatically released. Global references and weak global references are valid until programmers actively release them. JNI's local reference and global reference can prevent the corresponding instance in Java from being recycled by the JVM's garbage collection mechanism, but weak global reference allows its corresponding instance to be recycled.

Local reference

        To obtain the reference of a Java instance, the JNI layer is to obtain the jclass class corresponding to the Java instance. There are many ways to obtain the jclass.

         In JNI, except for the array, String, Class and Throwable of java basic types, other Java objects are represented by jobobjects in JNI. Therefore, one way of jclass Class is to obtain it from jobobject, as shown below:

extern "C"
JNIEXPORT jboolean JNICALL
Java_android_set(
        JNIEnv *env, jobject thiz, jobject ref, jstring module_name) {
    ...
    jclass clazz = env->GetObjectClass(thiz);
    ...
}

        thiz corresponds to an instance object in Java. It is represented by a jobobject in the JNI layer. The jobobject is converted to a jclass through the GetObjectClass method of env.

         Or get jclass by adding the full package name and class name in Java, as shown below:

extern "C"
JNIEXPORT jobject JNICALL
Java_....getList(JNIEnv *env, jobject instance) {
  ....
  jclass list_jclass = env->FindClass("java/util/List");
  ....
}

        The reference list of Java layer classes obtained in the above two ways_ Both jclass and clazz are local references. When the program leaves the program block, the list_jclass and clazz can no longer be used because they have been released by the JVM.

Global reference

        When multiple threads of the same process in JNI need to repeat operations on classes of the same type in Java, the local reference needs to be upgraded to a global reference, as shown below:

jstring  
MyNewString(JNIEnv *env, jchar *chars, jint len)  
{  
    static jclass stringClass = NULL;  
    ...  
    if (stringClass == NULL) {  
        jclass localRefCls =  
            (*env)->FindClass(env, "java/lang/String");  
        if (localRefCls == NULL) {  
            return NULL; /* exception thrown */  
        }  
        /* Create a global reference */  
        stringClass = (*env)->NewGlobalRef(env, localRefCls);  
        /* Local references are no longer useful */  
        (*env)->DeleteLocalRef(env, localRefCls);  
         ...  
    }  
    ...  
}  

        The above indicates that the NewGlobalRef method is used to convert a local reference localRefCls into a global reference stringClass method. At this time, the stringClass reference will not be released even after the code exits the code block. You can use the DeleteGlobalRef method of env to release the global reference.

         It should be noted that env's DeleteLocalRef method is used here to actively release a JNI local reference. The scenarios for the DeleteLocalRef method are as follows:

1. Create a large number of JNI local references (in the loop body or callback function), even if they are not used at the same time. Because the JVM needs enough space to track all JNI references, it may cause memory overflow or stack overflow.

2. If a JNI local reference is created for a large Java object, the reference must also be released manually after use, otherwise the GC will not recycle the Java object for a long time, which will also cause memory leakage.

Weak global reference

        

JNIEXPORT void JNICALL  
Java_mypkg_MyCls_f(JNIEnv *env, jobject self)  
{  
    static jclass myCls2 = NULL;  
    if (myCls2 == NULL) {  
        jclass myCls2Local =  
            (*env)->FindClass(env, "mypkg/MyCls2");  
        if (myCls2Local == NULL) {  
            return; /* can't find class */  
        }  
        myCls2 = NewWeakGlobalRef(env, myCls2Local);  
        if (myCls2 == NULL) {  
            return;
        }  
    }  
    ... 
}  

        The above represents the method of upgrading a local reference to a weak global reference using NewWeakGlobalRef. Weak global references, like global references, can be used across native methods and across threads. The difference from the global reference is that the weak global reference will not prevent the garbage collection of the underlying object, so you need to check whether the weak global reference is valid before using it. Use DeleteWeakGlobalRef to release a weak global reference.

Posted by Yaak on Thu, 04 Nov 2021 06:59:18 -0700