1. 程式人生 > >JNI(5)The Invocation API

JNI(5)The Invocation API

呼叫API允許軟體提供商載入Java VM 到任意的本地應用中。供應商可以提供支援Java的應用程式而無需連結Java VM的程式碼。

概述

下面程式碼展示了通過呼叫API如何使用函式。這個例子中C++程式碼建立了一個Java VM 和呼叫一個靜態方法,方法為Main.test.為了程式碼簡潔,省略了錯誤檢查。

    #include <jni.h>       /* where everything is defined */
    ...
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/usr/lib/java";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
    env->CallStaticVoidMethod(cls, mid, 100);
    /* We are done. */
    jvm->DestroyJavaVM();

這個例子使用介面中的三個方法,呼叫API允許本地應用使用JNI介面指標去訪問VM的特性。設計類似於網景的JRI嵌入介面。

建立 VM

JNI_CreateJavaVM()方法載入和初始化java VM和返回一個指向JNI介面指標的指標。JNI_CreateJavaVM()在主執行緒裡面呼叫。

新增到 VM

JNI介面指標(JNIEnv)在當前執行緒中有效。如果另外一個執行緒要訪問它,那麼它必須先呼叫AttachCurrentThread()新增自己到VM中,並且得到JNI介面指標。一旦新增到VM,本地執行緒執行和java執行緒執行在本地方法一樣。本地執行緒保持著VM的引用,知道呼叫DetachCurrentThread()方法,才會取消之間的關聯關係。

附加的執行緒堆疊應該有足夠的空間來執行一個合理數量的任務。作業系統給每個執行緒分配指定的空間。例如,使用執行緒,棧大小可以通過pthread_attr_t引數指定。

從VM中分離

一個本地執行緒要從VM中分離出來需要呼叫DetachCurrentThread(),分離必須是在退出之前。如果有java方法在棧中執行,那麼執行緒不能自我分離。

解除安裝VM

JNI_DestroyJavaVM()解除安裝 Java VM. As of JDK/JRE 1.1, 只有主執行緒能夠呼叫DestroyJavaVM解除安裝java VM. As of JDK/JRE 1.2, 可以在任何執行緒呼叫 DestroyJavaVM

解除安裝 VM.

The VM waits until the current thread is the only non-daemon user thread before it actually unloads. User threads include both Java threads and attached native threads. This restriction exists because a Java thread or attached native thread may be holding system resources, such as locks, windows, and so on. The VM cannot automatically free these resources. By restricting the current thread to be the only running thread when the VM is unloaded, the burden of releasing system resources held by arbitrary threads is on the programmer.

庫和版本管理

As of JDK/JRE 1.1, 一旦本地庫載入, 所有的類載入器都能夠看到它。因此不同的兩個類在不同的類載入器中有可能連結的是同一個本地方法。這樣會導致兩個問題:

  • 一個類在不同的類載入器中可能會錯誤的連結本地庫。
  • 本地方法可以很容混淆不同類載入器載入的類。這樣打破了類載入器提供的名稱空間分離,並且導致型別安全問題。

As of JDK/JRE 1.2, 每一個類載入器管理自己的本地庫。同一個JNI本地庫在一個類載入器中不能重複載入。如果那樣做,會導致UnsatisfiedLinkError 丟擲,例如,通常載入本地庫到兩個類裝入器,System.loadLibrary 會丟擲UnsatisfiedLinkError。這樣做的好處是:

  • 基於類載入器的名稱空間分離是儲存在本地庫裡面。一個原生的庫無法從不同的類載入器裡面混淆類。
  • 此外,當他們對應的類載入器被垃圾回收之後也會解除安裝。

為了提高版本控制和資源管理,JNI庫在JDK/JRE 1.2 裡面提供了以下兩個方法:

JNI_OnLoad

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

當原生庫載入的時候,VM才呼叫JNI_OnLoad(例如,通過System.loadLibrary).JNI_OnLoad必須返回原生庫需要的JNI版本。

要是新的JNI函式,原生庫JNI_OnLoad函式必須返回JNI_VERSION_1_2。如果原生庫沒有JNI_OnLoad函式,那麼VM會假定庫需要的是JNI_VERSION_1_1. 如果VM不能識別JNI_OnLoad方法返回的值,那麼原生庫將不能被載入。

LINKAGE:

Exported from native libraries that contain native method implementation.

SINCE:

JDK/JRE 1.4

使用在J2SE1.2中介紹的JNI方法,除了JDK/JRE 1.1中的功能,原生包的JNI_OnLoad方法必須返回JNI_VERSION_1_2.

使用在J2SE1.4中介紹的JNI方法,除了JDK/JRE 1.2中的功能,原生包的JNI_OnLoad方法必須返回JNI_VERSION_1_4.

如果原生包沒有JNI_OnLoad 函式, VM會認為包需要JNI的版本為JNI_VERSION_1_1.如果VM 不能識別JNI_OnLoad函式放回的版本號,那麼原生包將不能被載入。

JNI_OnUnload

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

VM在原生包被垃圾回收的時候呼叫JNI_OnUnload函式。這個函式可以用來執行清理操作。因為這個函式被一個不知道的上下文呼叫(比如從一個終結器),程式設計師應該保守使用Java VM服務,和避免任意Java回撥。

注意:JNI onload和JNI onunload是兩個函數出自JNI庫,而不是VM。

LINKAGE:

Exported from native libraries that contain native method implementation.

Invocation API Functions

指標JavaVM型別呼叫API函式表。下面的程式碼示例顯示了該函式表。

typedef const struct JNIInvokeInterface *JavaVM;


const struct JNIInvokeInterface ... = {
    NULL,
    NULL,
    NULL,

    DestroyJavaVM,
    AttachCurrentThread,
    DetachCurrentThread,

    GetEnv,

    AttachCurrentThreadAsDaemon
};


注意這三個呼叫API的函式:JNI_GetDefaultJavaVMInitArgs()JNI_GetCreatedJavaVMs(), 和 JNI_CreateJavaVM(), 不是 JavaVM 函式表的一部分. 這些函式在javaVM之前就已經可以使用。

JNI_GetDefaultJavaVMInitArgs

jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

返回Java VM的預設配置。在使用這個函式之前,需要制定JNI版本的版本號( 設定這個屬性vm_args->version),它表明VM需要的JNI版本號。這個方法返回之後,vm_args->version的值是VM支援的版本值。

LINKAGE:

Exported from the native library that implements the Java virtual machine.

PARAMETERS

vm_args: a pointer to a JavaVMInitArgs structure in to which the default arguments are filled.

RETURNS:

Returns JNI_OK if the requested version is supported; returns a JNI error code (a negative number) if the requested version is not supported.

JNI_GetCreatedJavaVMs

jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

返回所有已經建立的Java vm。Returns all Java VMs that have been created. 所有的VM指標按照它們建立的順序儲存在vmBuf緩衝區中。VM 的總數是存放在*nVms 中。

在JDK / JRE 1.2, 在一個程序中建立多個VM是支援的。

LINKAGE:

Exported from the native library that implements the Java virtual machine.

PARAMETERS:

vmBuf: pointer to the buffer where the VM structures will be placed.

bufLen: the length of the buffer.

nVMs: a pointer to an integer.

RETURNS:

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

JNI_CreateJavaVM

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

載入和初始化java VM. 當前執行緒成為主執行緒。設定主執行緒的JNI介面指標env引數。

在JDK/JRE 1.2 ,在一個程序中建立多個VM是支援的。

第二個引數JNI_CreateJavaVM 總是指向 JNIEnv *,而第三個引數是一個指標JavaVMInitArgs結構,使用選項字串編碼任意VM啟動選項:

typedef struct JavaVMInitArgs {
    jint version;

    jint nOptions;
    JavaVMOption *options;
    jboolean ignoreUnrecognized;
} JavaVMInitArgs;

 version至少設定 JNI_VERSION_1_2.options 的型別是:

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

陣列的大小是nOptions欄位表示的。如果ignoreUnrecognized 為 JNI_TRUEJNI_CreateJavaVM 忽略所有以 "-X" 或者 "_"開頭的不能識別的選項字串。 如果ignoreUnrecognized 為JNI_FALSEJNI_CreateJavaVM 遇到不能識別的字串會立刻返回JNI_ERR 。所有的java VMs必須都識別下面的標準的引數:

optionString meaning
-D<name>=<value> 設定一個系統屬性
-verbose[:class|gc|jni] 啟用詳細的輸出。 The options can be followed by a comma-separated list of names indicating what kind of messages will be printed by the VM. For example, "-verbose:gc,class" instructs the VM to print GC and class loading related messages. Standard names include: gcclass, and jni. All nonstandard (VM-specific) names must begin with "X".
vfprintf extraInfo is a pointer to the vfprintf hook.
exit extraInfo is a pointer to the exit hook.
abort extraInfo is a pointer to the abort hook.

 

此外,每個VM實現可能會支援自己的一套標準選項字串。非標準的選項名稱必須開始以“- x”或下劃線(“_”)。例如,JDK/JRE 支援-Xms 和-Xmx 允許指定初始的和最大的堆記憶體大小。以"-X"的選項都可以在java命令列中執行。

下面是示例程式碼,建立一個Java VM在JDK / JRE:

JavaVMInitArgs vm_args;
JavaVMOption options[4];

options[0].optionString = "-Djava.compiler=NONE";           /* disable JIT */
options[1].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
options[2].optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
options[3].optionString = "-verbose:jni";                   /* print JNI-related messages */

vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 4;
vm_args.ignoreUnrecognized = TRUE;

/* Note that in the JDK/JRE, there is no longer any need to call
 * JNI_GetDefaultJavaVMInitArgs.
 */
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res < 0) ...

LINKAGE:

Exported from the native library that implements the Java virtual machine.

PARAMETERS:

p_vm: VM 結構的地址.

p_env: JNI介面的地址(主執行緒中).

vm_args: VM初始化引數.

RETURNS:

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

DestroyJavaVM

jint DestroyJavaVM(JavaVM *vm);

解除安裝一個Java VM並收回其資源。

The support for DestroyJavaVM was not complete in JDK/JRE 1.1. As of JDK/JRE 1.1 Only the main thread may call DestroyJavaVM.Since JDK/JRE 1.2, any thread, whether attached or not, can call this function. If the current thread is attached, the VM waits until the current thread is the only non-daemon user-level Java thread. If the current thread is not attached, the VM attaches the current thread and then waits until the current thread is the only non-daemon user-level thread. 

JDK / JRE 1.1不完全支援DestroyJavaVM. 在JDK/JRE 1.1 只有主執行緒可以叫DestroyJavaVM。 從JDK/JRE 1.2, 任何執行緒都可以呼叫這個函式, 不管是否從依附的執行緒中. 如果與當前執行緒連線, VM等到當前執行緒是唯一非守護程序。如果沒有與當前執行緒相連線,那麼VM會與當前執行緒相連,並且等待當前執行緒是非守護執行緒. The JDK/JRE still does not support VM unloading, however.

LINKAGE:

Index 3 in the JavaVM interface function table.

PARAMETERS:

vm: the Java VM that will be destroyed.

RETURNS:

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

在JDK / JRE 1.1.2解除安裝虛擬機器不支援。

AttachCurrentThread

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

Attaches the current thread to a Java VM. Returns a JNI interface pointer in the JNIEnv argument.

Trying to attach a thread that is already attached is a no-op.

A native thread cannot be attached simultaneously to two Java VMs(本機執行緒不能同時與兩個Java虛擬機器連線。).

When a thread is attached to the VM, the context class loader is the bootstrap loader.

LINKAGE:

Index 4 in the JavaVM interface function table.

PARAMETERS:

vm: the VM to which the current thread will be attached.

p_env: pointer to the location where the JNI interface pointer of the current thread will be placed.

thr_args: can be NULL or a pointer to a JavaVMAttachArgs structure to specify additional information:

As of JDK/JRE 1.1, the second argument to AttachCurrentThread is always a pointer to JNIEnv. The third argument to AttachCurrentThread was reserved, and should be set to NULL.

As of JDK/JRE 1.2, you pass NULL as the third argument for 1.1 behavior, or pass a pointer to the following structure to specify additional information:

typedef struct JavaVMAttachArgs {
    jint version;  /* must be at least JNI_VERSION_1_2 */
    char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */
    jobject group; /* global ref of a ThreadGroup object, or NULL */
} JavaVMAttachArgs

RETURNS:

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

AttachCurrentThreadAsDaemon

jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);

Same semantics as AttachCurrentThread, but the newly-created java.lang.Thread instance is a daemon.

If the thread has already been attached via either AttachCurrentThread or AttachCurrentThreadAsDaemon, this routine simply sets the value pointed to bypenv to the JNIEnv of the current thread. In this case neither AttachCurrentThread nor this routine have any effect on the daemon status of the thread.

LINKAGE:

Index 7 in the JavaVM interface function table.

PARAMETERS:

vm: the virtual machine instance to which the current thread will be attached.

penv: a pointer to the location in which the JNIEnv interface pointer for the current thread will be placed.

args: a pointer to a JavaVMAttachArgs structure.

RETURNS

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

EXCEPTIONS

None.

SINCE:

JDK/JRE 1.4

DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

Detaches the current thread from a Java VM. All Java monitors held by this thread are released. All Java threads waiting for this thread to die are notified.

As of JDK/JRE 1.2 , the main thread can be detached from the VM.

 

LINKAGE:

Index 5 in the JavaVM interface function table.

PARAMETERS:

vm: the VM from which the current thread will be detached.

RETURNS:

Returns JNI_OK on success; returns a suitable JNI error code (a negative number) on failure.

GetEnv

jint GetEnv(JavaVM *vm, void **env, jint version);

LINKAGE:

Index 6 in the JavaVM interface function table.

PARAMETERS:

vm: The virtual machine instance from which the interface will be retrieved.
env: pointer to the location where the JNI interface pointer for the current thread will be placed.
version: The requested JNI version.

RETURNS:

If the current thread is not attached to the VM, sets *env to NULL, and returns JNI_EDETACHED. If the specified version is not supported, sets *env to NULL, and returns JNI_EVERSION. Otherwise, sets *env to the appropriate interface, and returns JNI_OK.

SINCE:

JDK/JRE 1.2

轉載