Java调用本地接口(JNI) (一)

来源:互联网 发布:跑鞋矩阵图2017 编辑:程序博客网 时间:2024/05/13 05:52

 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)利用。

The previous context describes this image.

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

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的实现代码为:

Code Example 2-1 Implementing a Native Method Using 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对象,局部引用,全局引用