缓存字段ID和方法ID

获取字段ID和方法ID时,需要用字段、方法的名字和描述符进行一个检索。检索过程相对比较费时,因此本节讨论用缓存技术来减少这个过程带来的消耗。
缓存字段ID和方法ID的方法主要有两种。两种区别主要在于缓存发生的时刻,是在字段ID和方法ID被使用的时候,还是定义字段和方法的类静态初始化的时候。 也就是使用时缓存 还是 类的静态初始化过程中缓存字段和方法ID 

 

使用时缓存
字段ID和方法ID可以在字段的值被访问或者方法被回调的时候缓存起来。下面的代码中把字段ID存储在静态变量当中,这样当本地方法被重复调用时,不必重新搜索字段ID:

JNIEXPORT void JNICALL  Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj) 
 { 
     static jfieldID fid_s = NULL; /* cached field ID for s */ 
  
     jclass cls = (*env)->GetObjectClass(env, obj); 
     jstring jstr; 
     const char *str; 
  
     if (fid_s == NULL) { 
         fid_s = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;"); 
         if (fid_s == NULL) { 
             return; /* exception already thrown */ 
         } 
     } 
     
     printf("In C:\n"); 
  
     jstr = (*env)->GetObjectField(env, obj, fid_s); 
     str = (*env)->GetStringUTFChars(env, jstr, NULL); 
     if (str == NULL) { 
         return; /* out of memory */ 
     } 
     printf("  c.s = \"%s\"\n", str); 
     (*env)->ReleaseStringUTFChars(env, jstr, str); 
  
     jstr = (*env)->NewStringUTF(env, "123"); 
     if (jstr == NULL) { 
         return; /* out of memory */ 
     } 
     
   (*env)->SetObjectField(env, obj, fid_s, jstr); 
 } 

 同样的思想,我们也可以缓存 java.lang.String 的构造方法的ID:

 jstring MyNewString(JNIEnv *env, jchar *chars, jint len) 
 { 
     jclass stringClass; 
     jcharArray elemArr; 
     static jmethodID cid = NULL; 
     jstring result; 
  
     stringClass = (*env)->FindClass(env, "java/lang/String"); 
     if (stringClass == NULL) { 
         return NULL; /* exception thrown */ 
     } 
     
     /* Note that cid is a static variable */ 
     if (cid == NULL) { 
         /* Get the method ID for the String constructor */ 
         cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V"); 
         if (cid == NULL) { 
             return NULL; /* exception thrown */ 
         } 
     } 
     
     /* Create a char[] that holds the string characters */ 
     elemArr = (*env)->NewCharArray(env, len); 
     if (elemArr == NULL) { 
         return NULL; /* exception thrown */ 
     } 
     (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars); 
  
     /* Construct a java.lang.String object */ 
     result = (*env)->NewObject(env, stringClass, cid, elemArr); 
  
     /* Free local references */ 
     (*env)->DeleteLocalRef(env, elemArr); 
     (*env)->DeleteLocalRef(env, stringClass); 
     return result; 
 } 

 当MyNewString方法第一次被调用时,我们计算 java.lang.String 的构造方法的ID,并存储在静态变量 cid中。

 

 

类的静态初始化过程中缓存字段和方法ID 
例如,为了缓存InstanceMethodCall.callback 的方法ID,我们引入了一个新的本地方法initIDs,这个方法在InstanceMethodCall 的静态初始化过程中被调用。代码如下:

class InstanceMethodCall { 
     private static native void initIDs(); 
     private native void nativeMethod(); 
     private void callback() { 
         System.out.println("In Java"); 
     } 
     public static void main(String args[]) { 
         InstanceMethodCall c = new InstanceMethodCall(); 
         c.nativeMethod(); 
     } 
     static { 
         System.loadLibrary("InstanceMethodCall"); 
         initIDs(); 
     } 
 } 

initIDs方法简单地计算并缓存方法ID:

jmethodID MID_InstanceMethodCall_callback; 
  
JNIEXPORT void JNICALL  Java_InstanceMethodCall_initIDs(JNIEnv *env, jclass cls) 
 { 
     MID_InstanceMethodCall_callback = (*env)->GetMethodID(env, cls, "callback", "()V"); 
 } 

VM进行静态初始化时在调用任何方法前调用 initIDs,这样方法ID 就被缓存了全局变量中,本地方法的实现就不必再进行 ID计算:

JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj) 
 { 
     printf("In C\n"); 
     (*env)->CallVoidMethod(env, obj,MID_InstanceMethodCall_callback); 
 } 

 

比起静态初始时缓存来说,使用时缓存有一些缺点:
1、使用时缓存的话,每次使用时都要检查一下。
2、方法ID和字段 ID在类被unload时就会失效,如果你在使用时缓存 ID,你必须确保只要本地代码依赖于这个 ID的值,那么这个类不被会 unload(下一章演示了如何通过使用 JNI函数创建一个类引用来防止类被 unload)。另一方面,如果缓存发生在静态初始化时,当类被 unload和reload 时,ID会被重新计算。
因此,尽可能在静态初始化时缓存字段 ID和方法 ID
 

posted on 2012-12-22 14:07  Cynthia&Sky  阅读(1199)  评论(0编辑  收藏  举报

导航