JNI引用

JNI提供了一些实例和数组类型(jobject、jclass、jstring、jarray 等)作为不透明的引用供本地代码使用。
本地代码永远不会直接操作引用指向的 VM内部的数据内容
要进行这些操作,必须通过使用 JNI操作一个不透明的引用来间接操作数据内容。因为只操作引用,你不必担心特定 JVM中对象的存储方式等信息。
合理地管理 JNI引用是写出高质量的代码的基础。

 

1.JNI 支持三种引用:局部引用、全局引用、弱全局引用(下文简称“弱引用”)。
2.局部引用和全局引用有不同的生命周期。当本地方法返回时,局部引用会被自动释放。而全局引用和弱引用必须手动释放。
3.局部引用或者全局引用会阻止 GC回收它们所引用的对象,而弱引用则不会。
4.不是所有的引用可以被用在所有的场合。例如,一个本地方法创建一个局部引用并返回后,再对这个局部引用进行访问是非法的。

 

局部引用
大多数JNI函数会创建局部引用。例如,NewObject 创建一个新的对象实例并返回一个对这个对象的局部引用。
局部引用只有在创建它的本地方法返回前有效。本地方法返回后,局部引用会被自动释放。
你不能在本地方法中把局部引用存储在静态变量中缓存起来供下一次调用时使用。

释放一个局部引用有两种方式,一个是本地方法执行完毕后 VM自动释放,另外一个是程序员通过 DeleteLocalRef手动释放。
既然VM会自动释放局部引用,为什么还需要手动释放呢?因为局部引用会阻止它所引用的对象被 GC回收。
局部引用只在创建它们的线程中有效,跨线程使用是被禁止的。不要在一个线程中创建局部引用并存储到全局引用中,然后到另外一个线程去使用。

关于局部引用溢出的问题,后面我们会单独再介绍

 

全局引用
全局引用可以跨方法、跨线程使用,直到它被手动释放才会失效。同局部引用一样,全局引用也会阻止它所引用的对象被 GC 回收。
与局部引用可以被大多数 JNI函数创建不同,全局引用只能使用一个 JNI函数创建:NewGlobalRef

下面这个版本的 MyNewString 演示了怎么样使用一个全局引用:

/* This code is OK */ 
 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); 
  
         /* The local reference is no longer useful */ 
         (*env)->DeleteLocalRef(env, localRefCls); 
  
         /* Is the global reference created successfully? */ 
         if (stringClass == NULL) { 
             return NULL; /* out of memory exception thrown */ 
         } 
     } 
 ... 
} 

上面这段代码中,一个由 FindClass返回的局部引用被传入 NewGlobalRef,用来创建一个对String 类的全局引用。删除 localRefCls后,我们检查
NewGlobalRef是否成功创建 stringClass。


弱引用
弱引用使用NewGlobalWeakRef创建,使用DeleteGlobalWeakRef释放。与全局引用类似,弱引用可以跨方法、线程使用。与全局引用不同的是,弱引用不会阻止GC回收它所指向的 VM内部的对象。
当本地代码中缓存的引用不一定要阻止 GC回收它所指向的对象时,弱引用就是一个最好的选择。


引用比较
给定两个引用(不管是全局、局部还是弱引用),你可以使用 IsSameObject 来判断它们两个是否指向相同的对象。例如:
(*env)->IsSameObject(env, obj1, obj2)
如果obj1和obj2指向相同的对象,上面的调用返回 JNI_TRUE(或者 1),否则返回JNI_FALSE(或者 0)。

JNI中的一个引用NULL 指向JVM中的null 对象。如果obj是一个局部或者全局引用,你可以使用(*env)->IsSameObject(env, obj, NULL)或者obj == NULL来判断obj是否指向一个 null对象。

在这一点儿上,弱引用有些有同,一个NULL弱引用同样指向一个JVM中的null对象,但不同的是,在一个弱引用上面使用IsSameObject时,返回值的意义是不同的:
(*env)->IsSameObject(env, wobj, NULL)
上面的调用中,如果 wobj已经被回收,会返回 JNI_TRUE,如果wobj 仍然指向一个活动对象,会返回 JNI_FALSE。

 

JDK提供了一系列的函数来管理局部引用的生命周期。这些函数包括:
EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame。

 

当你的本地代码不再需要一个全局引用时,你应该调用 DeleteGlobalRef 来释放它。如果你没有调用这个函数,即使这个对象已经没用了,JVM也不会
回收这个全局引用所指向的对象。

当你的本地代码不再需要一个弱引用时,应该调用 DeleteWeakGlobalRef 来释放它,如果你没有调用这个函数,JVM仍会回收弱引用所指向的对象,但
弱引用本身在引用表中所占的内存永远也不会被回收。

 

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

导航