本文是《The Java Native Interface Programmer’s Guide and Specification》读书笔记


JNI支持将类实例和数组类型(如jobject,jclass,jstring,jarray)作为不透明的引用。本地代码不直接检查不透明指针的内容,而是通过使用JNI方法来得到一个指向数据结构的指针的不透明引用。也就是说JNI操作的内容都是引用,因此我们只需要知道在JNI中有哪些引用。JNI支持三种不透明的引用:本地引用、全局引用,弱全局引用。

 本地引用和全局引用有不同的生命周期,本地引用是自释放的,而全局和弱全局引用需要程序员主动释放掉,否则会一起存在于程序中。本地引用和全局引用的对象不会被垃圾回收器回收(也就是只要存在本地引用或全局引用,这个对象就被认为是活跃的),而弱全局引用允许引用对象被垃圾回收器回收。需要注意的是,引用并不是在所有的上下文中都是有效的,也就是引用是有生命周期的,比如,你不能在本地代码(JNI方法的实现代码)中使用完本地引用后,将这个引用通过return语句返回(本地引用在使用完后,已经结束了生命周期,系统中将不存在这个引用了)。

本地引用(Local References)

大部分的JNI方法(函数)都会创建本地引用,比如,通过JNI方法NewObject创建一个新的实例,并返回这个实例的引用。我们在本地方法里不能通过静态变量来保存一个本地引用。下面是一个错误的例子。

jstring
MyNewString(JNIEnv *env, jchar *chars, jint len)
{
//试图通过一个静态变量来保存本地引用
static jclass stringClass = NULL;
jmethodID cid;
....
//判断本地引用是否为空
if (stringClass == NULL) {
stringClass = (*env)->FindClass(env,
"java/lang/String");
if (stringClass == NULL) {
return NULL; /* exception thrown */
}
}
/* 这里就会发生错误,因为stringClass指向的对象可能不存在了*/
cid = (*env)->GetMethodID(env, stringClass ,
"<init>", "([C)V");
....
}
上面的代码中,试图通过一个静态变量来保存本地引用,这也许可以减小重复调用`FindClass`方法的开销,这个方法在第一次调用时不会出错,可以正常执行。但当第二次调用这个方法时,就会出错了。因为本地引用在这个本地方法执行完后就自动释放了,虽然stringClass保存了上一次执行的引用,但它指向的那个对象可能由于没有任何引用,被垃圾回收器回收了,所以在第二次执行时,试图去找到一个并不存在的对象,肯定是会出错的。并且本地引用只存在于创建时所在的线程,不能在其他线程中使用。因此不能通过试图将本地引用保存在全局变量中,以便在其他线程中使用。

全局引用(Global References)

我们可以通过全局引用来保存调用本地方法的某些结果,以便在下次调用时使用。同样的,全局引用可以在多个线程中使用(不局限于创建时所在的线程),并且在程序员主动释放前,都会一直有效。可以通过下面的代码来创建一个全局引用:
/* 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 */
}
/* 用一个本地引用参数来创建一个全局引用 */
stringClass = (*env)->NewGlobalRef(env, localRefCls);
/* 释放掉本地引用 */
(*env)->DeleteLocalRef(env, localRefCls);
/* 判断全局引用是否创建成功 */
if (stringClass == NULL) {
return NULL; /* out of memory exception thrown */
}
}
...
}

弱全局引用(Weak Global References)

弱全局引用与全局引用类似,也可以在线程间共享和多次调用本地方法时共享。但也有区别,首先全局引用是通过JNI方法`NewGlobalRef`创建的,而弱全局引用是通过JNI方法`NewGlobalWeakRef`,并通过JNI方法`DeleteGlobalWeakRef`释放;其次弱全局引用并不会保证所引用的对象不会被垃圾回收器回收。因此我们在使用弱全局引用时,需要检查引用所反射的对象是否还存活。

杂谈

给定两个本地,全局或弱全局引用,我们可以通过JNI方法1isSameObject来判断这两个引用是不是指向同一个对象。返回JNI_TRUE,JNI_FALSE,NULL分别表示指向同一个对象,不是指向同一个对象,指向一个空对象。

Java里的引用类型

Java里一共有四种引用类型(摘录自以前的笔记,但不知道是从哪里看到的的):

  • 强引用,在代码中普遍存在,类似于Object object=new Object()这类的引用,只要强引用不存在,则GC永远不会回收掉引用的对象;
  • 软引用,用来描述一些有用但非必须的对象。对于软引用关联着的对象,在系统将在发生内存溢出时,将会把这些对象列入回收范围之中进行二次回收,如果这次还没有回收到足够的内存,才会抛出内存溢出异常。在JDK1.2后,提供了SofeReference来建立软引用;
  • 弱引用,也用来描述非必须对象,但强度比弱引用更弱一些,被弱引用关联的对象只能存活到下一次GC发生之前,当GC工作时,无论当时内存是否足够,都会被回收,可以用WeakReference来建立弱引用。
  • 虚引用,虚引用也叫幻影引用,这是最弱的一种引用,一个对象是否虚引用都不会对其生存时构成影响,也不能通过虚引用来取得一个对象实例。为一个对象设置虚引用的目的就是在这个对象被回收时收到一个通知,可以通过PhantomReference来建立虚引用。

Java Native Interface 四--JNI中引用类型的更多相关文章

  1. Java Native Interface 二 JNI中对Java基本类型和引用类型的处理

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 Java编程里会使用到两种类型:基本类型(如 ...

  2. Java Native Interface 六JNI中的异常

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 在这里只讨论调用JNI方法可能会出现的异常, ...

  3. Java Native Interface Specification(JNI)

    Java Native Interface Specification(JNI) 使用场景: 需要的功能,标准的java不能提供 有了一个用其他的语言写好的工具包,希望用java去访问它 当需要高性能 ...

  4. Java Native Interface 基于JNI的嵌入式手机软件开发实例

    1.通过JNI和c/c++的库组件.其他代码交互 2.java和c不能互通的原因时数据类型问题 Introduction https://docs.oracle.com/javase/8/docs/t ...

  5. Java Native Interface 五 JNI里的多线程与JNI方法的注册

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI里的多线程 在本地方法里写有关多线程的 ...

  6. Java Native Interface(JNI)

    JNI能让Java代码在Java虚拟机里调用其他编程语言(例如C.C++)写的应用或库,且不会影响任何Java虚拟机的实现. 什么时候用JNI? 1.应用程序所需的平台相关功能,标准的Java类库不支 ...

  7. 【详解】JNI (Java Native Interface) (二)

    案例二:传递参数给C代码,并从其获取结果 注:这里传递的参数是基本类型的参数,在C代码中有直接的映射类型. 此案例所有生成的所有文件如下: (1)编写案例二的Java代码,如下: 这里我们定义了一个n ...

  8. +Java中的native关键字浅析(Java+Native+Interface)++

    JNI是Java Native Interface的 缩写.从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的 ...

  9. Java Native Interface Specification Contents 翻译

    https://docs.oracle.com/en/java/javase/12/docs/specs/jni/index.html Google翻译 第1章:简介 本章介绍Java Native ...

随机推荐

  1. 如何查看当前Ubuntu系统的版本

    如何查看当前Ubuntu系统的版本 说来也惭愧,用Ubuntu差不多快1个月了,双系统是让朋友安的,只知道自己使用的是什么12版本的,具体怎么看还不知道,下面写一下查看当前Linux系统的版本的方法 ...

  2. linux内存管理

    一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分:    1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...

  3. [LeetCode] Bitwise AND of Numbers Range 数字范围位相与

    Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers ...

  4. 2-SAT

      n个布尔变量,满足m个如 A为x或B为y的限制 建一个点拆成两个,分别表示选TRUE或FALSE 建立A的!x B的y 的连边 与 A的x B的!y 的连边 每次dfs. 若一个点在之前条件下无论 ...

  5. iOS开发小技巧 - label中的文字添加点击事件

    Label中的文字添加点击事件 GitHub地址:https://github.com/lyb5834/YBAttributeTextTapAction 以前老师讲过类似的功能,自己懒得回头看了,找了 ...

  6. Redis 常用操作

    import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;import java.util.Set; ...

  7. com/android/dx/command/dexer/Main : Unsupported major.minor version 52.0

    这是由于JDK版本不一致造成的,指的是高版本的JDK编译的class不能放在低版本的JDK上运行. Version 52,表示JDK8编译的class不能运行在JDK7上,所以需要在本地安装JDK8.

  8. Android Studio JNI 开发简单案例

    转载:http://www.androidchina.net/5744.html 进程保活,热修复,硬件接入等等都需要底层的支持,而底层代码是 C .C++ 写的,那么在 Android 中怎么调用底 ...

  9. POJ 1966 Cable TV Network

    Cable TV Network Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 4702   Accepted: 2173 ...

  10. 自己对js对原型链的理解

    js对象分为2种 函数对象和普通对象 函数对象 比如 function Show(){}var x=function Show2(){}var b=new Function("show3&q ...