《Java 本地接口规范》- 调用 API
调用 API调用 API 允许软件厂商将 Java 虚拟机加载到任意的本地程序中。厂商可以交付支持 Java 的应用程序,而不必链接 Java 虚拟机源代码。本章首先概述了调用 API。然后是所有调用 API 函数的引用页。若要增强 Java 虚拟机的嵌
调用 API
调用 API 允许软件厂商将 Java 虚拟机加载到任意的本地程序中。厂商可以交付支持 Java 的应用程序,而不必链接 Java 虚拟机源代码。
本章首先概述了调用 API。然后是所有调用 API 函数的引用页。
若要增强 Java 虚拟机的嵌入性,可以用几种方式来扩展 JDK 1.1.2 中的调用 API。
概述
以下代码示例说明了如何使用调用 API 中的函数。在本例中,C++ 代码创建 Java 虚拟机并且调用名为 Main.test
的静态方法。为清楚起见,我们略去了错误检查。
#include <jni.h> /* 其中定义了所有的事项 */
...
JavaVM *jvm; /* 表示 Java 虚拟机*/
JNIEnv *env; /* 指向本地方法接口的指针 */
JDK1_1InitArgs vm_args; /* JDK 1.1 虚拟机初始化参数 */
vm_args.version = 0x00010001; /* 1.1.2 中新增的:虚拟机版本 */
/* 获得缺省的初始化参数并且设置类
* 路径 */
JNI_GetDefaultJavaVMInitArgs(&vm_args);
vm_args.classpath = ...;
/* 加载并初始化 Java 虚拟机,返回 env 中的
* JNI 接口指针 */
JNI_CreateJavaVM(&jvm, &env, &vm_args);
/* 用 JNI 调用 Main.test 方法 */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* 结束。*/
jvm->DestroyJavaVM();
本例使用了 API 中的三个函数。调用 API 允许本地应用程序用 JNI 接口指针来访问虚拟机特性。其设计类似于 Netscape 的 JRI 嵌入式接口。
创建虚拟机
JNI_CreateJavaVM() 函数加载并初始化Java 虚拟机,然后将指针返回到 JNI 接口指针。调用 JNI_CreateJavaVM()
的线程被看作主线程。
连接虚拟机
JNI 接口指针 (JNIEnv
) 仅在当前线程中有效。如果另一个线程需要访问 Java 虚拟机,则该线程首先必须调用AttachCurrentThread()
以将自身连接到虚拟机并且获得 JNI 接口指针。连接到虚拟机之后,本地线程的工作方式就与在本地方法内运行的普通 Java 线程一样了。本地线程保持与虚拟机的连接,直到调用 DetachCurrentThread()
时才断开连接。
卸载虚拟机
主线程不能自己断开与虚拟机的连接。而是必须调用DestroyJavaVM()
来卸载整个虚拟机。
虚拟机等到主线程成为唯一的用户线程时才真正地卸载。用户线程包括 Java 线程和附加的本地线程。之所以存在这种限制是因为 Java 线程或附加的本地线程可能正占用着系统资源,例如锁,窗口等。虚拟机不能自动释放这些资源。卸载虚拟机时,通过将主线程限制为唯一的运行线程,使释放任意线程所占用系统资源的负担落到程序员身上。
初始化结构
不同的 Java 虚拟机实现可能会需要不同的初始化参数。很难提出适合于所有现有和将来的 Java 虚拟机的标准初始化结构。作为一种折衷方式,我们保留了第一个域 (version
) 来识别初始化结构的内容。嵌入到 JDK 1.1.2 中的本地应用程序必须将版本域设置为 0x00010001
。尽管其它实现可能会忽略某些由 JDK 所支持的初始化参数,我们仍然鼓励虚拟机实现使用与 JDK 一样的初始化结构。
0x80000000
到 0xFFFFFFFF
之间的版本号需保留,并且不为任何虚拟机实现所识别。
以下代码显示了初始化 JDK 1.1.2 中的 Java 虚拟机所用的结构。
typedef struct JavaVMInitArgs {
/* 前两个域在 JDK 1.1 中保留,并
在 JDK 1.1.2 中正式引入。*/
/* Java 虚拟机版本 */
jint version;
/* 系统属性。*/
char **properties;
/* 是否检查 Java 源文件与已编译的类文件
*之间的新旧关系。*/
jint checkSource;
/* Java 创建的线程的最大本地堆栈大小。*/
jint nativeStackSize;
/* 最大 Java 堆栈大小。*/
jint javaStackSize;
/* 初始堆大小。*/
jint minHeapSize;
/* 最大堆大小。*/
jint maxHeapSize;
/* 控制是否校验 Java 字节码:
* 0 无,1 远程加载的代码,2 所有代码。*/
jint verifyMode;
/* 类加载的本地目录路径。*/
const char *classpath;
/* 重定向所有虚拟机消息的函数的钩子。*/
jint (*vfprintf)(FILE *fp, const char *format,
va_list args);
/* 虚拟机退出钩子。*/
void (*exit)(jint code);
/* 虚拟机放弃钩子。*/
void (*abort)();
/* 是否启用类 GC。*/
jint enableClassGC;
/* GC 消息是否出现。*/
jint enableVerboseGC;
/* 是否允许异步 GC。*/
jint disableAsyncGC;
/* 三个保留的域。*/
jint reserved0;
jint reserved1;
jint reserved2;
} JDK1_1InitArgs;
在 JDK 1.1.2 中,初始化结构提供了钩子,这样在虚拟机终止时,本地应用程序可以重定向虚拟机消息并获得控制权。
当本地线程与JDK 1.1.2 中的 Java 虚拟机连接时,以下结构将作为参数进行传递。实际上,本地线程与 JDK 1.1.2 连接时不需要任何参数。JDK1_1AttachArgs
结构仅由 C 编译器的填充槽组成,而 C 编译器不允许空结构。
typedef struct JDK1_1AttachArgs {
/*
* JDK 1.1 不需要任何参数来附加
* 本地线程。此处填充的作用是为了满足不允许空结构的 C
* 编译器的要求。
*/
void *__padding;
} JDK1_1AttachArgs;
调用 API 函数
JavaVM 类型是指向调用 API 函数表的指针。以下代码示例显示了这种函数表。
typedef const struct JNIInvokeInterface *JavaVM;
const struct JNIInvokeInterface ... = {
NULL,
NULL,
NULL,
DestroyJavaVM,
AttachCurrentThread,
DetachCurrentThread,
};
注意,JNI_GetDefaultJavaVMInitArgs()
、JNI_GetCreatedJavaVMs() 和JNI_CreateJavaVM()
这三个调用 API 函数不是 JavaVM 函数表的一部分。不必先有 JavaVM
结构,就可以使用这些函数。
JNI_GetDefaultJavaVMInitArgs
jintJNI_GetDefaultJavaVMInitArgs(void *vm_args);
返回 Java 虚拟机的缺省配置。在调用该函数之前,平台相关代码必须将 vm_args->version
域设置为它所期望虚拟机支持的 JNI 版本。在 JDK 1.1.2 中,必须将 vm_args->version
设置为 0x00010001
。(JDK1.1 不要求平台相关代码设置版本域。为了向后兼容性,如果没有设置版本域,则 JDK 1.1.2 假定所请求的版本为 0x00010001。JDK 的未来版本将要求把版本域设置为适当的值。) 该函数返回后,将把vm_args->version
设置为虚拟机支持的实际 JNI 版本。
参数:
vm_args:指向 VM-specific initialization
(特定于虚拟机的初始化)结构的指针,缺省参数填入该结构。
返回值:
如果所请求的版本得到支持,则返回“0”;如果所请求的版本未得到支持,则返回负数。
JNI_GetCreatedJavaVMs
jintJNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen,
jsize *nVMs);
返回所有已创建的Java 虚拟机。将指向虚拟机的指针依据其创建顺序写入 vmBuf 缓冲区。最多写入 bufLen 项。在 *nVMs 中返回所创建虚拟机的总数。
参数:
vmBuf:指向将放置虚拟机结构的缓冲区的指针。
返回值:
成功时返回“0”;失败则返回负数。
JNI_CreateJavaVM
jintJNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env,
void *vm_args);
加载并初始化Java 虚拟机。当前线程成为主线程。将env
参数设置为主线程的 JNI 接口指针。
JDK 1.1.2 不支持在单个进程中创建多个虚拟机。必须将 vm_args 中的版本域设置为 0x00010001
。
参数:
p_vm:指向位置(其中放置所得到的虚拟机结构)的指针。
p_env
:指向位置(其中放置主线程的 JNI 接口指针)的指针。
vm_args
: Java 虚拟机初始化参数。
返回值:
成功时返回“0”;失败则返回负数。
DestroyJavaVM
jintDestroyJavaVM(JavaVM *vm);
卸载 Java 虚拟机并回收资源。只有主线程能够卸载虚拟机。调用 DestroyJavaVM()
时,主线程必须是唯一的剩余用户线程。
参数:
vm:将销毁的 Java 虚拟机。
返回值:
成功时返回“0”;失败则返回负数。
AttachCurrentThread
jintAttachCurrentThread(JavaVM *vm, JNIEnv **p_env,
void *thr_args);
将当前线程连接到 Java 虚拟机。在 JNIEnv
参数中返回 JNI 接口指针。
参数:
vm:当前线程所要连接到的虚拟机。
p_env
:指向位置(其中放置当前线程的 JNI 接口指针)的指针。
thr_args
:特定于虚拟机的线程连接参数。
返回值:
成功时返回“0”;失败则返回负数。
DetachCurrentThread
jintDetachCurrentThread(JavaVM *vm);
断开当前线程与 Java 虚拟机之间的连接。释放该线程占用的所有 Java 监视程序。通知所有等待该线程终止的 Java 线程。
主线程(即创建 Java 虚拟机的线程)不能断开与虚拟机之间的连接。作为替代,主线程必须调用 JNI_DestroyJavaVM()
来卸载整个虚拟机。
参数:
vm:当前线程将断开连接的虚拟机。
返回值:
成功时返回“0”;失败则返回负数。
更多推荐
所有评论(0)