0
点赞
收藏
分享

微信扫一扫

02-JNI官方文档学习笔记

彪悍的鼹鼠 2022-04-17 阅读 92
javaandroid

目录

1 JNI概述

2 设计概述

2.1 JNI的接口函数与接口指针

2.2 Native方法的编译、加载、链接

2.2.1 解析Native方法名称

2.2.2 Native方法的形参

2.3 引用Java对象

2.3.1 全局引用和局部引用

2.3.2 本地引用的实现

2.4 访问Java对象

2.4.1 访问原始数组

2.4.2 访问字段和方法

2.5 报告程序错误

2.6 Java异常

3 JNI类型和数据结构

3.1 原始类型

3.3 字段和方法ID

3.4 Value值类型

3.5 类型签名

4 JNI函数

4.1 接口功能表

5 调用API

5.1 JNI_CreateJavaVM

5.2 AttachCurrentThread

5.3 DetachCurrentThread

5.4 JNI_OnLoad

5.5 JNI_OnLoad_L

5.6 JNI_OnUnload

5.7 JNI_OnUnload_L


1 JNI概述

        本章将介绍了Java Native interface(JNI)。JNI是一个native编程接口。它允许在Java虚拟机(VM)中运行的Java代码与用其他编程语言(如C、C++)编写的应用程序和库进行互操作。

        JNI最重要的好处是,它对底层Java VM的实现没有施加任何限制。因此,Java VM 厂商可以添加对JNI的支持,而不影响VM的其他部分。程序员可以编写一个本地应用程序或库的版本,并期望它与支持JNI的所有Java VM一起工作。

        在某些情况下,Java不能满足应用程序的需求。程序员使用JNI编写Java native方法来处理当应用程序不能完全用Java编写时的那些情况。

        下面的示例说明了您何时需要使用Java native方法:

      • 标准的Java类库不支持应用程序所需的依赖于平台的特性;
      • 已经有了一个用另一种语言编写的库,并希望通过JNI让Java代码访问它;
      • 希望用C/C++等程序集语言实现一小部分对性能要求高的关键性代码;

        通过通过JNI进行编程,您可以使用native方法:

      • 创建、检查和更新Java对象(包括数组和字符串);
      • 调用java方法;
      • 捕获及上抛异常;
      • 加载类及获取类信息;
      • 执行运行时类型检查;

        还可以通过调用API使JNI让任意的native应用程序嵌入至Java VM。这允许程序员能轻松地使用他们现有的应用程序,而不必链接到VM源代码。

2 设计概述

2.1 JNI的接口函数与接口指针

        native代码通过调用JNI函数来访问Java VM特性。可以通过一个接口指针来使用JNI函数一个接口指针是一个指向一个指针(Pointer X)的指针。这个指针(Pointer X)指向一个指针数组,每个指针都指向一个接口函数。每个接口函数都位于数组内部的预定义偏移量处。下图说明了接口指针的组织结构:

        JNI接口的管理类似于C++虚拟函数表或COM接口。使用接口表而不是硬连接函数项的优点是,JNI命名空间与native代码是独立的。虚拟机可以轻松地提供多个版本的JNI函数表。例如,VM可能支持两个JNI函数表:

      • 一个是执行彻底的非法参数检查,适合于调试;
      • 另一种是执行JNI规范所要求的最小的检查量,因此更有效;

        JNI接口指针仅在当前线程中有效。因此,一个native方法不能将接口指针从一个线程传递到另一个线程。实现JNI的虚拟机可以在JNI接口指针指向的区域内分配和存储线程本地数据。

        native方法接收JNI接口指针作为参数。当VM从同一个Java线程多次调用native方法时,它可以保证将相同的接口指针传递给native方法。但是,可以从不同的Java线程调用native方法,因此可能接收到不同的JNI接口指针。即native方法会涉及多线程操作,需要考虑并发情况。

2.2 Native方法的编译、加载、链接

        由于JavaVM是多线程的,所以native库也应该使用带多线程属性的编译器进行编译和链接。例如对于GNU GCC编译器,应该使用-D_REENTRANT或-D_POSIX_C_SOURCE标识来进行编译。

        使用System.loadLibrary()方法加载native库(静态库、动态库)。在下面的示例中,类初始化方法加载一个特定于平台的native库,库中定义了native方法f:

package p.q.r;
class A {
    native double f(int i, String s);
    static {
        System.loadLibrary("p_q_r_A");
    }
}

        System.loadLibrary的参数是由程序员任意选择的一个库名。

        程序员可以使用单个库来存储任意数量的类所需的所有本机方法,只要这些类要用相同的类加载器来加载即可。VM内部为每个类加载器维护一个加载native库列表。native名称应尽量减少名称冲突。同时支持动态链接库和静态链接的库。

2.2.1 解析Native方法名称

        JNI定义了一个从在Java中的native方法声明到位于native库中的native方法名的1:1映射关系VM使用此映射,动态地将Java调用的native方法链接到native库中相应的native实现

映射通过以下组件来生成native方法名:

      • 前缀(Java_);
      • 给定声明native方法的类的内部形式的二进制名称:转义该名称的结果;
      • 下划线(“_”);
      • 转义方法名称;
      • 如果native方法声明重载:两个下划线(“__”),后面是方法声明的转义参数描述符;

        转义使每个字母数字ASCII字符(A-Za-z0-9)保持不变,并用相应的转义序列替换下表中的每个UTF-16代码单元。如果要转义的名称包含一个代理对,则高代理代码单元和低代理代码单元将分别转义。转义的结果是一个只由ASCII字符a-Za-z0-9和下划线("_")组成的字符串。

        转义是必要的,原因有二。首先,为了确保Java源代码中可能包含Unicode字符的类和方法名,可以转换为C源代码中的有效函数名。其次,为了确保使用“;”和“[”字符作为参数类型的native方法可以在C函数名编码

        当Java程序调用native方法时,VM会首先搜索native方法名称的简短版本,即没有转义参数签名的名称。如果没有找到具有短名称的native方法,则VM将查找native方法名称的长版本,即包含转义参数签名的名称

        首先查找较短的名称可以使在native库中声明实现更容易。例如,给定了Java中的这个native方法:

package p.q.r;
class A {
    native double f(int i, String s);
}

        对应的C函数可以被命名:Java_p_q_r_A_f,而不是Java_p_q_r_A_f__ILjava_lang_String_2

        只有当一个类中的两个或多个native方法具有相同的名称时,才需要在native库中使用长名称来声明实现。例如,给定Java中的这些本机方法:

package p.q.r;
class A {
    native double f(int i, String s);
    native double f(int i, Object s);
}

        对应的C函数命名Java_p_q_r_A_f__ILjava_lang_String_2和Java_p_q_r_A_f__ILjava_lang_Object_2

        如果Java中的native方法只被非native方法重载,则不需要native库中的长名称

        native方法也可以使用RegisterNatives函数显式链接。请注意,RegisterNatives可以通过更改为给定Java方法执行的native代码来更改VM的行为(包括加密算法、正确性、安全性、类型安全性)。因此,谨慎使用使用RegisterNatives函数的native库的应用程序。

2.2.2 Native方法的形参

        JNI接口指针是指向native方法的第一个参数。JNI接口指针的类型是JNIEnv。第二个参数的不同取决于native方法是静态的还是非静态的。非静态native方法的第二个参数是对该对象的引用静态native方法的第二个参数是对其Java类的引用

        其余的参数分别对应于常规的Java方法参数。native方法调用通过返回值将其结果传递回调用例程。

下面的代码示例说明了使用C函数来实现本机方法f。native方法f的声明如下:

package p.q.r;
class A {
    native double f(int i, String s);
    // ...
}

        长名为Java_p_q_r_A_f_ILjava_lang_String_2的C函数实现了本机方法f:

jdouble Java_p_q_r_A_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 ...;
}

        请注意,我们总是使用接口指针env来操作Java对象。

        使用C++,您可以编写一个稍微干净一些的代码版本,如下面的代码示例所示

extern "C" /* specify the C calling convention */
jdouble Java_p_q_r_A_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 ... 
}

        使用C++时,额外的间接级别接口指针参数将从源代码中消失。然而,其潜在机制与C完全相同。在C++中,JNI函数被定义为扩展到C对应函数的内联成员函数。

2.3 引用Java对象

        原始类型,如整数、字符等,可以在Java和本机代码之间进行复制。另一方面,任意的Java对象都是通过引用来传递的。VM必须跟踪已传递给本机代码的所有对象,以便垃圾收集器不会释放这些对象。反过来,本机代码必须有一种方法来通知VM它不再需要这些对象。此外,垃圾收集器必须能够移动native代码引用的对象。

2.3.1 全局引用和局部引用

        JNI将native代码使用的对象引用分为两类:本地引用和全局引用。本地引用在native方法调用期间有效,并且在native方法返回后自动释放。全局引用在它们被显式释放之前一直保持有效

        对象将作为本地引用传递给native方法。由JNI函数返回的所有Java对象都是本地引用。JNI允许程序员从本地引用中创建全局引用。JNI函数期望Java对象同时接受全局引用和本地引用。native方法可以返回对VM的本地或全局引用作为其结果。

        在大多数情况下,程序员应该依赖VM在native方法返回后释放所有本地引用。但是,有时程序员应该显式地释放一个本地引用。例如,请考虑以下情况:

    • native方法访问大型Java对象,从而创建对Java对象的本地引用。然后,native方法在返回给调用者之前执行额外的计算。对大型Java对象的本地引用将防止该对象被垃圾收集,即使该对象在计算的其余部分中不再使用
    • native方法创建大量的本地引用,尽管不是所有引用都同时使用。由于虚拟机需要一定数量的空间来跟踪本地引用,因此创建过多的本地引用可能会导致系统内存不足

        JNI允许程序员在native方法中的任何地方手动删除本地引用。为了确保程序员可以手动释放本地引用,JNI函数不允许创建额外的本地引用,除了它们作为结果返回的引用之外。本地引用仅在创建它们的线程中有效。native代码不能将本地引用从一个线程传递到另一个线程。

2.3.2 本地引用的实现

        为了实现本地引用,Java VM为从Java到native方法的每个控制转换创建一个注册表。注册表将不可移动的本地引用映射到Java对象,并防止这些对象被垃圾收集。传递给native方法的所有Java对象(包括那些作为JNI函数调用的结果返回的对象)都将自动添加到注册表中。在native方法返回后删除注册表,允许垃圾收集其所有条目

        有不同的方法来实现注册表,例如使用表、链接列表或哈希表。尽管引用计数可用于避免注册表中的重复项,但JNI实现没有义务检测和折叠重复项。

2.4 访问Java对象

        JNI对全局和本地引用提供了一组丰富的访问器函数。这意味着无论VM如何在内部表示Java对象,相同的native方法实现都可以工作。这也是为什么JNI可以被各种VM实现所支持的一个关键原因。

        通过使用不透明的引用访问器函数的开销高于直接访问C数据结构的开销。但在大多数情况下,Java程序员使用native方法来执行重要的任务,我们认为这个接口的开销可以被接受。

2.4.1 访问原始数组

        对于包含许多原始数据类型的大型Java对象,如整数数组和字符串,这种开销是不可接受的(比如用于执行vector和矩阵计算的native方法)。遍历Java数组并使用函数调用检索每个元素的效率非常低。

        一种解决方案引入了“pinning”(固定)的概念,这样native方法就可以要求虚拟机查明数组的内容。然后,本机方法会接收到一个指向这些元素的直接指针。然而,这种方法有两个含义:

    • 垃圾收集器(GC)必须支持"pinning";
    • VM必须在内存中连续地布局原始数组。虽然大多数原始数组是这样布局的,但布尔型数组可以实现为打包或解打包。因此,依赖于精确布局的布尔型数组的native代码将不可移植的

        我们采取了一种克服了上述两个问题的折衷方案:

    • 首先,我们提供了一组函数来在Java数组的一个段区间和native内存缓冲区之间复制基本数组元素。如果native方法只需要访问大数组中的少量元素,请使用这些函数。
    • 其次,程序员可以使用另一组函数来检索固定版本元素的数组。请记住,这些函数可能需要JavaVM来执行存储分配和复制。这些函数是否实际上复制了数组,这取决于VM的实现:
      • 如果垃圾收集器支持固定,并且数组的布局与native方法的预期相同,则不需要复制。
      • 否则,数组将复制到不可移动的内存块(例如,在C堆中),并执行必要的格式转换。此时将返回一个指向该副本的指针。
    • 最后,接口提供功能,通知VM native代码不再需要访问数组元素。当您调用这些函数时,系统要么取消固定数组,要么将原始数组与其不可移动的副本进行协调,并释放副本。

        JNI实现必须确保在多个线程中运行的native方法可以同时访问同一个数组。例如,JNI可以为每个固定的数组保留一个内部计数器,这样一个线程就不会取消被另一个线程固定的数组。请注意,JNI不需要锁定基本数组以通过native方法进行独家访问。同时从不同的线程更新一个Java数组会导致不确定性的结果

2.4.2 访问字段和方法

        JNI允许native代码访问Java对象的字段及方法调用。JNI通过其符号名称类型签名来标识方法和字段。两步过程的成本是从名称和签名中定位字段或方法。例如,要调用类cls中的方法f,native代码首先获得一个方法ID,如下所示:

jmethodID mid = env->GetMethodID(cls, "f", "(ILjava/lang/String;)D");

然后,native代码可以重复使用方法ID,而无需花费方法查找的成本,如下所示:

jdouble result = env->CallDoubleMethod(obj, mid, 10, str);

        字段或方法ID不会阻止VM卸载已生成该ID的类。卸载类后,方法或字段ID将无效。因此,如果它打算长期使用一种方法或字段ID,native代码必须确保:

    • 保留对底层类的实时引用;
    • 或重新获取方法或字段ID;

        JNI没有对内部实现字段和方法id施加任何限制。

2.4.2.1 调用敏感方法

        少数的Java方法有一个特殊的属性,称为敏感性方法(caller sensitivity)。敏感方法可以根据其调用者的身份进行不同的行为。

        当native代码调用这样的方法时,调用堆栈上可能没有任何Java调用者。程序员有责任知道从它们的native代码中调用的Java方法是否是敏感方法,以及如果没有Java调用者,这些敏感方法将如何响应。如果有必要,程序员可以提供Java代码来调用native代码,进而调用原始的Java方法。

2.5 报告程序错误

        JNI不检查编程错误,如传递空指针或非法的参数类型。JNI不检查这些编程错误,原因如下:

    • 强制JNI函数检查所有可能的错误条件会降低正常(正确的)native方法的性能。
    • 在许多情况下,没有足够的运行时类型信息来执行这种检查

        程序员不能将错误类型的非法指针或参数传递给JNI函数。这样做可能会导致任意的后果,包括损坏的系统状态或VM崩溃。

2.6 Java异常

        JNI允许native方法引发任意的Java异常。native代码还可以处理突出的Java异常。未被处理的Java异常将被传播回VM。

3 JNI类型和数据结构

        本章讨论了JNI如何将Java类型映射到native C语音类型。

3.1 原始类型

        下表描述了Java原始类型及其依赖于机器的native等价物

        为方便起见,我们提供了以下定义:

#define JNI_FALSE 0 
#define JNI_TRUE 1

        jsize整数类型用于描述大小:

typedef jint jsize;

3.2 引用类型

        JNI包括许多对应于不同类型的Java对象的引用类型。JNI引用类型被组织在以下层次结构中:

        在C中,所有其他的JNI引用类型都被定义相同类型:jobject。例如:

typedef jobject jclass;

        在C++中,JNI引入了一组虚拟类来加强子类型关系。例如

class _jobject {}; 
class _jclass : public _jobject {};
// ... 
typedef _jobject *jobject; 
typedef _jclass *jclass;

3.3 字段和方法ID

        方法和字段id是常规的C指针类型

struct _jfieldID; /* opaque structure */ 
typedef struct _jfieldID *jfieldID; /* field IDs */ 
struct _jmethodID; /* opaque structure */ 
typedef struct _jmethodID *jmethodID; /* method IDs */

3.4 Value值类型

        jvalue联合类型被用作参数数组中的元素类型。其声明如下:

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

3.5 类型签名

        JNI使用Java VM对类型签名的表示。下表显示了这些类型的签名。

        例如,Java方法:

long f(int n, String s, int[] arr);

        具有以下类型签名:

4 JNI函数

4.1 接口功能表

        每个函数都可以通过JNIEnv参数以一个固定的偏移量进行访问。JNIEnv类型是一个指向存储所有JNI函数指针的结构的指针。其定义如下:

typedef const struct JNINativeInterface *JNIEnv;

        VM初始化函数表,如下代码示例所示。请注意,前三个条目是为将来与COM兼容而保留的

        请注意,该函数表可以在所有JNI接口指针之间进行共享

const struct JNINativeInterface ... = {

  NULL,
  NULL,
  NULL,
  NULL,
  GetVersion,

  DefineClass,
  FindClass,

  FromReflectedMethod,
  FromReflectedField,
  ToReflectedMethod,

  GetSuperclass,
  IsAssignableFrom,

  ToReflectedField,

  Throw,
  ThrowNew,
  ExceptionOccurred,
  ExceptionDescribe,
  ExceptionClear,
  FatalError,

  PushLocalFrame,
  PopLocalFrame,

  NewGlobalRef,
  DeleteGlobalRef,
  DeleteLocalRef,
  IsSameObject,
  NewLocalRef,
  EnsureLocalCapacity,

  AllocObject,
  NewObject,
  NewObjectV,
  NewObjectA,

  GetObjectClass,
  IsInstanceOf,

  GetMethodID,

  CallObjectMethod,
  CallObjectMethodV,
  CallObjectMethodA,
  CallBooleanMethod,
  CallBooleanMethodV,
  CallBooleanMethodA,
  CallByteMethod,
  CallByteMethodV,
  CallByteMethodA,
  CallCharMethod,
  CallCharMethodV,
  CallCharMethodA,
  CallShortMethod,
  CallShortMethodV,
  CallShortMethodA,
  CallIntMethod,
  CallIntMethodV,
  CallIntMethodA,
  CallLongMethod,
  CallLongMethodV,
  CallLongMethodA,
  CallFloatMethod,
  CallFloatMethodV,
  CallFloatMethodA,
  CallDoubleMethod,
  CallDoubleMethodV,
  CallDoubleMethodA,
  CallVoidMethod,
  CallVoidMethodV,
  CallVoidMethodA,

  CallNonvirtualObjectMethod,
  CallNonvirtualObjectMethodV,
  CallNonvirtualObjectMethodA,
  CallNonvirtualBooleanMethod,
  CallNonvirtualBooleanMethodV,
  CallNonvirtualBooleanMethodA,
  CallNonvirtualByteMethod,
  CallNonvirtualByteMethodV,
  CallNonvirtualByteMethodA,
  CallNonvirtualCharMethod,
  CallNonvirtualCharMethodV,
  CallNonvirtualCharMethodA,
  CallNonvirtualShortMethod,
  CallNonvirtualShortMethodV,
  CallNonvirtualShortMethodA,
  CallNonvirtualIntMethod,
  CallNonvirtualIntMethodV,
  CallNonvirtualIntMethodA,
  CallNonvirtualLongMethod,
  CallNonvirtualLongMethodV,
  CallNonvirtualLongMethodA,
  CallNonvirtualFloatMethod,
  CallNonvirtualFloatMethodV,
  CallNonvirtualFloatMethodA,
  CallNonvirtualDoubleMethod,
  CallNonvirtualDoubleMethodV,
  CallNonvirtualDoubleMethodA,
  CallNonvirtualVoidMethod,
  CallNonvirtualVoidMethodV,
  CallNonvirtualVoidMethodA,

  GetFieldID,

  GetObjectField,
  GetBooleanField,
  GetByteField,
  GetCharField,
  GetShortField,
  GetIntField,
  GetLongField,
  GetFloatField,
  GetDoubleField,
  SetObjectField,
  SetBooleanField,
  SetByteField,
  SetCharField,
  SetShortField,
  SetIntField,
  SetLongField,
  SetFloatField,
  SetDoubleField,

  GetStaticMethodID,

  CallStaticObjectMethod,
  CallStaticObjectMethodV,
  CallStaticObjectMethodA,
  CallStaticBooleanMethod,
  CallStaticBooleanMethodV,
  CallStaticBooleanMethodA,
  CallStaticByteMethod,
  CallStaticByteMethodV,
  CallStaticByteMethodA,
  CallStaticCharMethod,
  CallStaticCharMethodV,
  CallStaticCharMethodA,
  CallStaticShortMethod,
  CallStaticShortMethodV,
  CallStaticShortMethodA,
  CallStaticIntMethod,
  CallStaticIntMethodV,
  CallStaticIntMethodA,
  CallStaticLongMethod,
  CallStaticLongMethodV,
  CallStaticLongMethodA,
  CallStaticFloatMethod,
  CallStaticFloatMethodV,
  CallStaticFloatMethodA,
  CallStaticDoubleMethod,
  CallStaticDoubleMethodV,
  CallStaticDoubleMethodA,
  CallStaticVoidMethod,
  CallStaticVoidMethodV,
  CallStaticVoidMethodA,

  GetStaticFieldID,

  GetStaticObjectField,
  GetStaticBooleanField,
  GetStaticByteField,
  GetStaticCharField,
  GetStaticShortField,
  GetStaticIntField,
  GetStaticLongField,
  GetStaticFloatField,
  GetStaticDoubleField,

  SetStaticObjectField,
  SetStaticBooleanField,
  SetStaticByteField,
  SetStaticCharField,
  SetStaticShortField,
  SetStaticIntField,
  SetStaticLongField,
  SetStaticFloatField,
  SetStaticDoubleField,

  NewString,

  GetStringLength,
  GetStringChars,
  ReleaseStringChars,

  NewStringUTF,
  GetStringUTFLength,
  GetStringUTFChars,
  ReleaseStringUTFChars,

  GetArrayLength,

  NewObjectArray,
  GetObjectArrayElement,
  SetObjectArrayElement,

  NewBooleanArray,
  NewByteArray,
  NewCharArray,
  NewShortArray,
  NewIntArray,
  NewLongArray,
  NewFloatArray,
  NewDoubleArray,

  GetBooleanArrayElements,
  GetByteArrayElements,
  GetCharArrayElements,
  GetShortArrayElements,
  GetIntArrayElements,
  GetLongArrayElements,
  GetFloatArrayElements,
  GetDoubleArrayElements,

  ReleaseBooleanArrayElements,
  ReleaseByteArrayElements,
  ReleaseCharArrayElements,
  ReleaseShortArrayElements,
  ReleaseIntArrayElements,
  ReleaseLongArrayElements,
  ReleaseFloatArrayElements,
  ReleaseDoubleArrayElements,

  GetBooleanArrayRegion,
  GetByteArrayRegion,
  GetCharArrayRegion,
  GetShortArrayRegion,
  GetIntArrayRegion,
  GetLongArrayRegion,
  GetFloatArrayRegion,
  GetDoubleArrayRegion,
  SetBooleanArrayRegion,
  SetByteArrayRegion,
  SetCharArrayRegion,
  SetShortArrayRegion,
  SetIntArrayRegion,
  SetLongArrayRegion,
  SetFloatArrayRegion,
  SetDoubleArrayRegion,

  RegisterNatives,
  UnregisterNatives,

  MonitorEnter,
  MonitorExit,

  GetJavaVM,

  GetStringRegion,
  GetStringUTFRegion,

  GetPrimitiveArrayCritical,
  ReleasePrimitiveArrayCritical,

  GetStringCritical,
  ReleaseStringCritical,

  NewWeakGlobalRef,
  DeleteWeakGlobalRef,

  ExceptionCheck,

  NewDirectByteBuffer,
  GetDirectBufferAddress,
  GetDirectBufferCapacity,

  GetObjectRefType,

  GetModule
};

        其他具体函数,可查询相关API接口表。可下载:JNI官方手册 进行查询。

5 调用API

5.1 JNI_CreateJavaVM

jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

        加载并初始化一个Java VM。当前的线程成为了主线程。将env参数设置为主线程的JNI接口指针。不支持在单个进程中创建多个虚拟机JNI_CreateJavaVM的第二个参数总是指向JNIEnv*的指针,而第三个参数是指向JavaVMInitArgs结构的指针,该结构使用选项字符串来编码任意的VM启动选项。

typedef struct JavaVMInitArgs { 
    jint version; 
    jint nOptions; 
    JavaVMOption *options; 
    jboolean ignoreUnrecognized; 
} JavaVMInitArgs;

typedef struct JavaVMOption {
    /* the option as a string in the default platform encoding */
    char *optionString;  
    void *extraInfo;
} JavaVMOption;

5.2 AttachCurrentThread

jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);

        将当前线程附加到JavaVM上。返回JNIEnv参数中的JNI接口指针。尝试附加一个已经附加的线程是禁止操作的一个native线程不能同时连接到两个Java VM上。当线程附加到VM时,上下文类加载器就是引导加载程序。

5.3 DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

        从Java VM中分离出当前的线程。此线程所持有的所有Java监视器都将被释放。所有等待此线程死亡的Java线程都将得到通知主线程可以与VM分离试图分离未附加的线程是不可操作的。如果在调用DetachCurrentThread时出现异常,VM可以选择报告其存在。

5.4 JNI_OnLoad

jint JNI_OnLoad(JavaVM *vm, void *reserved);

        由动态链接的库定义的可选函数。当加载native库时,VM调用JNI_OnLoad(例如通过System.loadLibrary)

        为了使用在JNI API的特定版本中定义的函数,JNI_OnLoad必须返回一个至少定义该版本的常量。例如,希望使用JDK1.4中引入的AttachCurrentThreadAsDaemon函数,至少需要返回JNI_VERSION_1_4。如果native库不能导出JNI_OnLoad函数,则VM假定该库只需要JNI版本的JNI_VERSION_1_1如果VM不能识别JNI_OnLoad返回的版本号,则VM将卸载库,并像从未加载库一样

5.5 JNI_OnLoad_L

jint JNI_Onload_<L>(JavaVM *vm, void *reserved);

        必须由静态链接的库来定义的强制性函数

        如果一个名为“libDemo‘的库是静态链接的,那么在第一次调用System.loadLibrary(“libDemo”)时,一个JNI_OnLoad_L函数将被调用与JNI_OnLoad函数相同的参数和期望返回值。JNI_OnLoad_L必须返回native库所需的JNI版本。此版本必须是JNI_VERSION_1_8或更高版本。如果虚拟机不能识别JNI_OnLoad_L返回的版本号,则虚拟机将像从未加载库一样。

5.6 JNI_OnUnload

void JNI_OnUnload(JavaVM *vm, void *reserved);

        由动态链接的库定义的可选函数。当包含native库的类加载器被GC垃圾收集时,VM调用JNI_OnUnload。

        此函数可用于执行清理操作。因为这个函数是在一个未知的上下文中调用的(例如来自finalizer),所以程序员在使用Java VM服务时应该保持警惕,避免任意的Java回调。

5.7 JNI_OnUnload_L

void JNI_OnUnload_<L>(JavaVM *vm, void *reserved);

        静态链接的库定义的可选函数。当包含静态链接的本机库“libDemo”的类加载器被GC垃圾收集时,VM将调用该库的JNI_OnUnload_L函数。

        此函数可用于执行清理操作。因为这个函数是在一个未知的上下文中调用的(比如来自finalizer),所以程序员在使用Java VM服务时应该保持警惕,避免任意的Java回调。

举报

相关推荐

0 条评论