JNI - 缓存、 三大引用

在JNI中有很多方法会用到ID, 比如:getObjectField( jobject , fid) 获取java的字段, CallVoidMethod( jobject , mid ) 调用java的方法。可是在获取字段ID 和方法的ID时需要字段/方法名 和 描述 进行检索,这个过程相对比较耗时。如果还有频繁调用的情况,会大大降低方法的效率。此时我们需要使用缓存策略减少这个过程的消耗。

缓存策略根据缓存时机不同分为两种。一种:使用时缓存。第二种:初始化缓存。

使用时缓存

在调用方法时,将id写为静态变量,多次调用多线程调用id不用重新检索。但是在类被unload时会失效,在使用时要判空处理。下面代码缺少  env -> ReleaseStringUTFChars(Jstr,str);(局部变量方法使用完之后会自动回收,但会阻止它所引用的对象被回收。)

在java中调用

for( int i = 0 ; i < 100; i++){

          testCache();

}

结果输出:

初始化缓存

在静态代码块中加载库后直接调用初始化方法,Id变为全局变量,由于类被unload 和 reload时id会被从新计算无需担心。

结果输出 In C "哈哈哈啊"

局部引用

局部引用有JNI函数创建出来 getObject~  find~  new~ 这些创建出来的局部引用只能在本方法返回前有效。不可将局部引用缓存到静态变量中或存储到全局变量中,如:static jsclass clazz; 方法使用完后会被释放,下次进入方法时class已经是一个失效的引用,会引起程序崩溃。

代码创建大量局部引用时会造成引用表溢出,手动释放 env->DeleteLocalRef( obj )

for (int i = 0; i<len; i++){

       jobject obj = env ->newObject(....)

       env->DeleteLocalRef( obj )

 }

当然管理局部引用最合适的方法时使用Push/PopLocalFrame()函数。

#define NUM ( 20 );

f (JNIEmv *env , ...){

       jobject result;

       if(  env->PushlocalFrame(env,NUM) < 0){

         return;

       }

      result  =  env ->newObj~();

      .......省略........

      env ->PopLocalFrame( NULL);

}

全局引用

全局引用可以跨线程,跨方法使用,与局部引用一样会阻止他引用的对象被GC回收。创建方法:NewGlobalRef( ).( 如果在JNI中使用多线程需要重新获取env)。手动释放DeleteGlobalRef(glo);

弱引用 

与全局引用类似 NewGlobalWeakRef创建,跨线程,跨方法。特点:允许GC回收塔指向VM的对象。但在引用表中建立的映射不会回收,需要DeleteWeakGlobalRef( )。

 局部引用的内部实现 

一个对象从 JVM 传递给本地方法时,就把控制权移交了过去,JVM 会为每一个对象的传递创建 一条记录,一条记录就是一个本地代码中的引用和 JVM 中的对象的一个映射。记录中的对象不 会被 GC 回收。所有传递到本地代码中的对象和从 JNI 函数返回的对象都被自动地添加到映射表 中。当本地方法返回时,VM 会删除这些映射,允许 GC 回收记录中的数据。图 11.5 演示了局 部引用记录是怎么样被创建和删除的。一个 JVM 窗口对应一个本地方法,窗口里面包含了一个 指向局部引用映射表的指针。方法 D.f 调用本地方法 C.g。C.g 通过 C 函数 Java_C_g 来实现。 在进入到 Java_C_g 之前,虚拟机会创建一个局部引用映射表,当 Java_C_g 返回时,VM 会删 掉这个局部引用映射表。

猜你喜欢

转载自blog.csdn.net/m0_37312601/article/details/81637880
JNI