1. Java Native Interface介绍
在以下的情况使用JNI:
- 标准Java类库不支持的平台依赖特性的应用
- 已经用其他语言编写好了一个库,希望用Java通过JNI访问
- 你希望实现对时间效率要求严格的一小部分,比如用汇编
通过本地接口编程,你可以:
- 创建,检查,更新java对象,包括数组和字符串
- 调用Java方法
- 捕获和抛出异常
- 装载类,获得类信息
- 执行运行时类型检查
- 把一个应用嵌入Java VM
2. 设计概览
2.1 JNI Interface Functions and Pointers
Java VM通过JNI函数(JNI Functions)访问本地代码,JNI函数通过接口指针(interface pointer)利用。
Interface Pointer是指向指针的指针,类似C++的VFT(virtual function table)或者COM接口
2.2 加载和连接本地方法
通过System.loadLibrary方法加载,RegisterNatives()可以注册本地方法和一个类联合起来。
2.2.1 解析本地方法名
本地方法名由以下部分组成
- the prefix
Java_
- a mangled fully-qualified class name
- an underscore (“_”) separator
- a mangled method name
- for overloaded native methods, two underscores (“__”) followed by the mangled argument signature
- the prefix
Unicode转换
Escape Sequence
|
Denotes
|
---|---|
_0XXXX
|
a Unicode character
XXXX .
Note that lower case is used to represent non-ASCII Unicode characters, e.g., _0abcd as opposed to
_0ABCD .
|
_1
|
the character “_”
|
_2
|
the character “;” in signatures
|
_3
|
the character “[“ in signatures
|
2.2.2 本地方法参数
例如本地方法声明为:
package pkg;
class Cls {
native double f(int i, String s);
...
}
C的实现代码为:
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( JNIEnv *env, /* interface pointer */ jobject obj, /* "this" pointer */ jint i, /* argument #1 */ jstring s) /* argument #2 */ { /* Obtain a C-copy of the Java string */ const char *str = (*env)->GetStringUTFChars(env, s, 0); /* process the string */ ... /* Now we are done with str */ (*env)->ReleaseStringUTFChars(env, s, str); return ... } C++的代码为:
Code Example 2-2 Implementing a Native Method Using C++extern "C" /* specify the C calling convention */ jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( JNIEnv *env, /* interface pointer */ jobject obj, /* "this" pointer */ jint i, /* argument #1 */ jstring s) /* argument #2 */ { const char *str = env->GetStringUTFChars(s, 0); ... env->ReleaseStringUTFChars(s, str); return ... }
2.3 引用Java对象(Reference Java Objects)
元数据类型如integers, charactes等,在java和本地代码之见是拷贝传递的。另一方面,任意的JAVA对象是通过参数引用传递的。
2.3.1 全局和局部引用(Global and Local References )
JNI给本地代码使用的对象引用分为两类:局部引用和全局引用。局部引用在本地方法调用期间是有效的,并且在本地方法返回后,自动释放。全局引用保持有效直到被显式释放。
对象是作为局部引用传递给本地方法的。JNI函数返回的所有JAVA对象都是局部引用。JNI允许程序员从局部引用创建全局引用。JNI函数接受全局和本地两种引用。一个本地方法的返回到VM中结果可以是一个局部或者全局的引用作。
大多数情况下,程序员可以依赖VM在本地函数返回后自动释放所有的局部引用。但是,在有些时候程序员需要显示地释放局部引用,比如下面的例子:
- 一个本地方法访问一个大型的JAVA对象,因此创建一个对该对象的局部引用。然后这个本地方法在返回前执行另外的计算。对该对象的本地引用将阻止该对象作为垃圾回收,即使这个对象在计算的剩余部分长时间不使用。
- 一个本地放发创建大量局部引用,但是并不是所有的对象都会同时使用。因为VM需要确定数量的空间来保持对本地引用的跟踪,创建很多的引用可能使系统内存溢出。例如,一个本地方法在一个大对象数组中循环,得到的元素是局部引用,并且每一步迭代时在元素上操作。在每个迭代后,程序员不再需要这个数组元素的局部引用。
JNI允许程序员在任意点上手动地删除一个本地方法的局部引用。为了确保程序员可以手动地释放,JNI函数不允许创建额外的引用,除非引用作为返回结果。
局部引用只在创建它的那个线程中是有效的。本地代码绝不可以传递局部引用从一个线程到另一个线程。
2.3.2 实现局部引用
为实现局部引用,JAVA VM为从JAVA到本地方法的控制的每个转换创建一个注册表。一个注册信息将非移动性的局部引用影射到JAVA对象,并且保持到这个对象被回收。所有的JAVA对象是自动增加到注册表中的,包括那些JNI函数调用的返回结果。注册信息在本地方法返回后被删除,允许所有的条目被垃圾回收。
有不同的方法实现注册表,比如TABLE,链表列表或者一个Hash表。尽管引用记数可以避免重复注册,JNI仍然不得不检查和防止重复的条目。
注意,局部引用不能够被本地堆栈上的适当扫描如实地实现,本地代码可以把局部引用保存到全局或者堆数据结构中。
--------------------------------------------------------
JNI,jni调用本地接口,JNI调用动态连接库,JNI调用DLL,JAVA对象,局部引用,全局引用