JNI (Java Native Interface)
Java源码层 —— JNI层 —— Native层
JNI(Java Native Interface)是Jave本地接口,是Java与其它语言之间相互调用的桥梁。
主要作用
- 解决无法用Java语言调用Java语言不支持的依赖于操作系统平台特性的一些功能的问题;
- 方便整合旧的非Java语言编写的系统代码;
- 为了提示性能,采用其他语言(C或C++)在处理一些功能上运行效率更高。
Native方法注册
Navtive方法的注册将Java中的Native方法通过方法指针与JNI进行关联。
- 静态注册 —— 通过javac命令编译和javah命令生成JNI方法的.h文件,方法首次调用时进行关联,即保存JNI的函数指针
- 动态注册 —— 用JNINativeMethod数据结构记录Java中的Native方法和JNI方法的关联,在调用System.loadLibrary()方法后,会查找加载的so库中的JNI_OnLoad方法,如果存在则调用该方法,其最终调用JNIEnv的RegisterNatives方法来完成动态注册。
数据类型转换
Java的数据类型到了JNI层就需要转换为JNI层的数据类型。基本数据类型的转换处理void类型,其他只需在Java的类型前添加“j”即可。引用数据类型同样需在Java的类型前添加“j”即可,数组类型以“Array”结尾,除Class、String和Throwable外的其他所有类型都用“jobject”表示。引用数据类型拥有有继承关系。
Java | Native | 签名 |
byte | jbyte | B |
char | jchar | C |
short | jshort | S |
int | jint | I |
float | jfloat | F |
double | jdouble | D |
long | jlong | J |
boolean | jboolean | Z |
void | void | V |
Java | Native | 签名 |
所有对象 | jobject | L+classname+; |
Class | jclass | Ljava/lang/Class; |
String | jstring | Ljava/lang/String; |
Throwable | jthrowable | Ljava/lang/Throwable; |
Object[] | jobjectArray | [L+classname+; |
byte[] | jbyteArray | [B |
char[] | jcharArray | [C |
short[] | jshortArray | [S |
int[] | jintArray | [I |
float[] | jfloatArray | [F |
double[] | jdoubleArray | [D |
long[] | jlongArray | [J |
boolean[] | jbooleanArray | [Z |
方法签名
因为Java语法支持方法重载,单单通过方法名通常无法准确找到对应方法,所有引入了方法签名概念,作用是通过方法签名可以准确定位方法。
方法签名的格式是:(参数类型签名格式…)返回类型签名格式
JNIEnv
JNIEnv代表了在Native中的Java环境。JNIEnv在jni.h文件中被定义,在C中JNIEnv类型是JNINativeInterface结构,在JNINativeInterface结构中定义了一系列和JNIEnv对应的函数指针,通过这些函数指针的定义,就能查找到Java虚拟机中的JNI函数表,而从实现JNI层在Java虚拟机中方法的调用。在调用Java类的成员变量和方法是通过初始化方法中获取jfieldID和jmethodID来获取变量引用和调用对应方法,可以调用GetObjectField方法传入jfieldID获取对应Java类的成员变量,调用CallVoidMethod或CallStaticVoidMethod方法传入jclass和jmethodID调用在Java对应类的类的方法或类方法(静态方法)。注意的是在不同线程中的JNIEnv并不是同一个,因此它不能跨线程,是线程独立的。
JNI中的引用类型
- 本地引用——类似Java方法中的局部变量的引用,当Native方法返回时,本地引用会被自动释放,只能在创建它的线程中有效不能跨线程使用,受JVM管理。JNIEnv基本上大部分方法调用返回的引用都是本地引用。
- 全局引用——类型Java类中的成员变量,当Native方法返回时,全局引用不会被自动释放,也不会被GC回收,所以需要手动进行释放,可以跨线程使用,不受JVM管理。全局引用在JNIEnv中调用NewGlobalRef方法来创建,调用DeleteGlobalRef方法来释放。
- 弱全局引用——是一种特殊的全局引用,可以被GC回收,因此在使用该引用前需要调用JNIEnv的IsSameObject方法来进行非空判断。弱全局引用在JNIEnv中调用NewWeakGlobalRef方法来创建,调用DeleteWeakGlobalRef方法来释放。
Java虚拟机中的本地方法栈
本地方法栈在Java运行时内存模型中与Java虚拟机栈及其相似,不同点是Java虚拟机栈中操作的是Java方法,而本地方法栈操作Native方法。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。有些虚拟机将Java虚拟机栈和本地方法栈合二为一。