In JNI, how do I cache the class, methodID, and fieldIDs per IBM's performance recommendations?

前端 未结 5 1787
庸人自扰
庸人自扰 2020-12-02 12:40

I read on on IBM that

To access Java objects\' fields and invoke their methods, native code must make calls to FindClass(), GetFieldID(), GetMethod

5条回答
  •  没有蜡笔的小新
    2020-12-02 13:20

    Here is how I practice IBM's recommendation. Considering demo java class like that:

    public class SimpleClazz {
    
        public int value = 10;
    
        public native int getValue();
    
        static {
            // Load Native Library
            System.loadLibrary("The native library name");
        }
    }
    

    The corresponding jni header file like that:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include 
    /* Header for class SimpleClazz */
    
    #ifndef _Included_SimpleClazz
    #define _Included_SimpleClazz
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     SimpleClazz
     * Method:    getValue
     * Signature: ()I
     */
    JNIEXPORT jint JNICALL Java_SimpleClazz_getValue
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    According to IBM's recommendation, we need to cache the used class SimpleClazz and the field id of the object member value.

    After learning this good article, I cache the SimpleClazz in the function JNI_OnLoad, which is called when the native library is loaded (for example, through System.loadLibrary). In the JNI_Onload, we do find class and store this jclass as a global field.

    Furthermore, in the native implementation of getValue, we use static local variable to cache the field id of value. This design is to make sure this filed id can be in the better scope, rather than in the global scope. The drawback of this design is that we need to compare with NULL each time we call this function. I learnt this design from section 4.4.1 of the book The Java Native Interface: Programmer's Guide and Specification.

    Finally, we also need to write the function JNI_OnUnload, which is called when the class loader containing the native library is garbage collected. In this function, we release the global reference of jclass.

    My cpp implementation is shown as below:

    #include 
    #include 
    
    static jclass simpleCls;
    
    // According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnLoad
    // The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
        JNIEnv* env;
        if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
            return JNI_ERR;
        } else {
            jclass localSimpleCls = (*env)->FindClass("SimpleClazz");
    
            if (localSimpleCls == NULL) {
                return JNI_ERR;
            }
            simpleCls = (jclass) (*env)->NewGlobalRef(env, localSimpleCls);
        } 
        return JNI_VERSION_1_6;
    }
    
    
    
    JNIEXPORT jint JNICALL Java_SimpleClazz_getValue(JNIEnv * env, jobject thiz){
        static jfieldID valueID = NULL;
        if (valueID == NULL) {
            valueID = (*env)->GetFieldID(env, simpleCls, "value", "I");
            if (valueID == NULL){
                return JNI_ERR;         // Exception thrown
            }
        }
        jint value = (*env)->GetIntField(env, thiz, valueID);
        return value;
    }
    
    // According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnUnload
    // The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
    void JNI_OnUnload(JavaVM *vm, void *reserved) {
        JNIEnv* env;
        if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
            // Something is wrong but nothing we can do about this :(
            return;
        } else {
            if (0 != NULL){
                (*env)->DeleteGlobalRef(env, simpleCls);
            }
        }
    }
    

提交回复
热议问题