Skip to content

Latest commit

 

History

History
155 lines (115 loc) · 6.49 KB

6.预备知识-JNI进阶.md

File metadata and controls

155 lines (115 loc) · 6.49 KB

局部与全局引用

JNI 支持三种引用:

  • 局部引用:通过 NewLocalRef 和各种 JNI 接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。会阻止 GC 回收所引用的对象,不在本地函数中跨函数使用,不能跨线程使用。函数返回后局部引用所引用的对象会被 JVM 自动释放,或调用 DeleteLocalRef 释放。(*env)->DeleteLocalRef(env,local_ref)
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
jcharArray charArr = (*env)->NewCharArray(env, len);
jstring str_obj = (*env)->NewObject(env, cls_string, cid_string, elemArray);
jstring str_obj_local_ref = (*env)->NewLocalRef(env,str_obj);   // 通过NewLocalRef函数创建
  • 全局引用**:** 调用 NewGlobalRef 基于局部引用创建,会阻GC回收所引用的对象。可以跨方法、跨线程使用。JVM不会自动释放,必须调用 DeleteGlobalRef 手动释放 (*env)->DeleteGlobalRef(env,g_cls_string);
static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
    jclass cls_string = (*env)->FindClass(env, "java/lang/String");
    g_cls_string = (*env)->NewGlobalRef(env,cls_string);
}
  • 弱全局引用:调用 NewWeakGlobalRef 基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在JVM认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef 手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)
static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
    jclass cls_string = (*env)->FindClass(env, "java/lang/String");
    g_cls_string = (*env)->NewWeakGlobalRef(env,cls_string);
}

异常处理

多线程

NIO 操作

JNI 与 NULL

3.2.10 局部和全局引用

在 native 方法中,我们经常使用 FindClass(), GetMethodID(), GetFieldID() 来获取 jclass, jmethodID 和 jfieldID。这些方法的调用成本很高,我们应该获取一次并且将其缓存以供后续使用,而不是重复执行调用,从而消除开销。

JNI 中 native 代码使用的对象引用分为两种:局部引用和全局引用:

  1. 局部引用在 native 方法中创建,并在退出 native 方法时销毁。可以使用 DeleteLocalRef() 显示的使局部引用失效,以便可以进行垃圾回收。
  2. 全局引用只有显示使用 DeleteGlobalRef() 才会被销毁。可以通过 NewGlobalRef() 从局部引用创建全局引用。

public class TestJNIReference {
   static {
      System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
   }
 
   // A native method that returns a java.lang.Integer with the given int.
   private native Integer getIntegerObject(int number);
 
   // Another native method that also returns a java.lang.Integer with the given int.
   private native Integer anotherGetIntegerObject(int number);
 
   public static void main(String args[]) {
      TestJNIReference test = new TestJNIReference();
      System.out.println(test.getIntegerObject(1));
      System.out.println(test.getIntegerObject(2));
      System.out.println(test.anotherGetIntegerObject(11));
      System.out.println(test.anotherGetIntegerObject(12));
      System.out.println(test.getIntegerObject(3));
      System.out.println(test.anotherGetIntegerObject(13));
   }
}

上面的代码有两个 native 方法,它们都返回 java.lang.Integer 对象。

在 C/C++ 代码中,我们通过 FindClass() 需要获取 java.lang.Integer 的引用。然后找到 Integer 的构造函数ID。我们希望将这些都缓存起来以消除开销。

下面代码是不起作用的:

#include <jni.h>
#include <stdio.h>
#include "TestJNIReference.h"
 
// Global Reference to the Java class "java.lang.Integer"
static jclass classInteger;
static jmethodID midIntegerInit;
 
jobject getInteger(JNIEnv *env, jobject thisObj, jint number) {
 
   // Get a class reference for java.lang.Integer if missing
   if (NULL == classInteger) {
      printf("Find java.lang.Integer\n");
      classInteger = (*env)->FindClass(env, "java/lang/Integer");
   }
   if (NULL == classInteger) return NULL;
 
   // Get the Method ID of the Integer's constructor if missing
   if (NULL == midIntegerInit) {
      printf("Get Method ID for java.lang.Integer's constructor\n");
      midIntegerInit = (*env)->GetMethodID(env, classInteger, "<init>", "(I)V");
   }
   if (NULL == midIntegerInit) return NULL;
 
   // Call back constructor to allocate a new instance, with an int argument
   jobject newObj = (*env)->NewObject(env, classInteger, midIntegerInit, number);
   printf("In C, constructed java.lang.Integer with number %d\n", number);
   return newObj;
}
 
JNIEXPORT jobject JNICALL Java_TestJNIReference_getIntegerObject
          (JNIEnv *env, jobject thisObj, jint number) {
   return getInteger(env, thisObj, number);
}
 
JNIEXPORT jobject JNICALL Java_TestJNIReference_anotherGetIntegerObject
          (JNIEnv *env, jobject thisObj, jint number) {
   return getInteger(env, thisObj, number);
}

上述代码中,我们调用 FindClass() 来获取 java.lang.Integer 的引用,并保存在全局的静态变量中。尽管如此,在下一次调用中,此引用不再有效(并且不是NULL)。这是因为FindClass()返回一个本地引用,一旦该方法退出就会失效。

为了解决这个问题,我们需要从FindClass()返回的局部引用创建一个全局引用。然后我们可以释放局部引用。修改后的代码如下:

 // Get a class reference for java.lang.Integer if missing
   if (NULL == classInteger) {
      printf("Find java.lang.Integer\n");
      // FindClass returns a local reference
      jclass classIntegerLocal = (*env)->FindClass(env, "java/lang/Integer");
      // Create a global reference from the local reference
      classInteger = (*env)->NewGlobalRef(env, classIntegerLocal);
      // No longer need the local reference, free it!
      (*env)->DeleteLocalRef(env, classIntegerLocal);
   }

参考资料

todo

  • 仔细阅读参考资料