(JNI)The Invocation API

譯文地址:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#wp9502

<h1>第五章<h1>

Invocation API允許軟件供應(yīng)商將Java VM加載到任意本機(jī)應(yīng)用程序中。供應(yīng)商可以提供支持Java的應(yīng)用程序,而不必與Java VM源代碼鏈接。
本章從Invocation API的概述開始。接下來是所有Invocation API函數(shù)的參考頁面為了增強(qiáng)Java VM的可嵌入性,Invoke API在JDK 1.1.2中以少量方式擴(kuò)展。

概觀

以下代碼示例說明了如何在Invocation API中使用函數(shù)。在這個(gè)例子中,C ++代碼創(chuàng)建一個(gè)Java VM并調(diào)用一個(gè)稱為Main.test的靜態(tài)方法。為了清楚起見,我們省略錯(cuò)誤檢查。

#include <jni.h> / *其中一切都定義* /
...
JavaVM * jvm; / *表示Java VM * /
JNIEnv * env; / *指向本機(jī)方法的指針* /
JDK1_1InitArgs vm_args; / * JDK 1.1 VM初始化參數(shù)* /
vm_args.version = 0x00010001; / * 1.1.2中的新功能:VM版本* /
/ *獲取默認(rèn)的初始化參數(shù)并設(shè)置類
 *路徑* /
JNI_GetDefaultJavaVMInitArgs(&vm_args);
vm_args.classpath = ...;
/ *加載和初始化Java VM,返回一個(gè)JNI接口
 *指針在env * /
JNI_CreateJavaVM(&jvm,&env,&vm_args);
/ *調(diào)用Main.test方法使用JNI * /
jclass cls = env-> FindClass(“Main”);
jmethodID mid = env-> GetStaticMethodID(cls,“test”,“(I)V”);
env-> CallStaticVoidMethod(cls,mid,100);
/* 我們完了。 * /
jvm-> DestroyJavaVM();

此示例在API中使用三個(gè)函數(shù)。 Invocation API允許本機(jī)應(yīng)用程序使用JNI接口指針來訪問VM功能。該設(shè)計(jì)類似于Netscape的JRI嵌入式界面。

創(chuàng)建虛擬機(jī)

JNI_CreateJavaVM()函數(shù)加載并初始化Java VM,并返回指向JNI接口指針的指針。調(diào)用JNI_CreateJavaVM()的線程被認(rèn)為是主線程。

連接到虛擬機(jī)

JNI接口指針(JNIEnv)僅在當(dāng)前線程中有效。如果另一個(gè)線程需要訪問Java VM,則必須首先調(diào)用AttachCurrentThread()將其自身附加到VM并獲取JNI接口指針。一旦連接到VM,本機(jī)線程就像在本機(jī)方法中運(yùn)行的普通Java線程一樣工作。本機(jī)線程仍然連接到VM,直到它調(diào)用DetachCurrentThread()來自行分離。
附加的線程應(yīng)該有足夠的堆棧空間來執(zhí)行可重復(fù)的工作量。每個(gè)線程的堆棧空間分配是操作系統(tǒng)特定的。例如,使用pthreads,可以在pthread_attr_t參數(shù)中指定堆棧大小為pthread_create。

卸載虛擬機(jī)

主線程無法從虛擬機(jī)分離。相反,它必須調(diào)用DestroyJavaVM()來卸載整個(gè)VM。
VM等待直到主線程在實(shí)際卸載之前是唯一的用戶線程。用戶線程包括Java線程和附加的本機(jī)線程。存在此限制,因?yàn)镴ava線程或附加的本機(jī)線程可能正在保存系統(tǒng)資源,例如鎖,窗口等。虛擬機(jī)無法自動釋放這些資源。通過將主線程限制為VM卸載時(shí)唯一運(yùn)行的線程,釋放由任意線程保存的系統(tǒng)資源的負(fù)擔(dān)在程序員身上。

<h1>庫和版本管理<h1>

在JDK 1.1中,一旦加載本地庫,它就可以從所有的類加載器中看到。因此,不同類加載器中的兩個(gè)類可以與相同的本機(jī)方法鏈接。這會導(dǎo)致兩個(gè)問題:
類可能會錯(cuò)誤地鏈接到由不同類加載器中具有相同名稱的類加載的本機(jī)庫。
本機(jī)方法可以輕松地從不同的類加載器混合類。這打破了類裝載機(jī)提供的名稱空間分離,并導(dǎo)致類型安全問題。
在JDK中,每個(gè)類加載器都管理自己的一組本機(jī)庫。相同的JNI本地庫不能加載到多個(gè)類加載器中。這樣做會引起“不滿意的鏈接錯(cuò)誤”。例如,System.loadLibrary在將本機(jī)庫加載到兩個(gè)加載器中時(shí)拋出一個(gè)UnsatisfiedLinkError。新方法的好處是:基于類加載器的名稱空間分隔保留在本機(jī)庫中。一個(gè)本機(jī)庫不能輕易地混合來自不同類加載器的類。
另外,本地庫可以在相應(yīng)的類加載器被垃圾回收時(shí)卸載。
為了方便版本控制和資源管理,Java 2 Platform中的JNI庫可以選擇導(dǎo)出以下兩個(gè)功能:
JNI_OnLoad
--
jint JNI_OnLoad(JavaVM * vm,void * reserved);
加載本地庫時(shí),VM會調(diào)用JNI_OnLoad(例如,通過System.loadLibrary)。 JNI_OnLoad必須返回本機(jī)庫所需的JNI版本。
為了使用任何新的JNI函數(shù),本機(jī)庫必須導(dǎo)出返回JNI_VERSION_1_2的JNI_OnLoad函數(shù)。如果本機(jī)庫不導(dǎo)出JNI_OnLoad函數(shù),則虛擬機(jī)假定庫僅需要JNI版本JNI_VERSION_1_1。如果VM無法識別JNI_OnLoad返回的版本號,則無法加載本機(jī)庫。

聯(lián)系:
從包含本機(jī)方法實(shí)現(xiàn)的本機(jī)庫導(dǎo)出。

從JDK / JRE 1.4:
為了使用J2SE版本1.2中引入的JNI函數(shù),除了JDK 1.1中可用的JNI函數(shù)之外,本機(jī)庫必須導(dǎo)出返回JNI_VERSION_1_2的JNI_OnLoad函數(shù)。

為了使用J2SE 1.4版中引入的JNI函數(shù),除了版本1.2中可用的JNI函數(shù)之外,本機(jī)庫必須導(dǎo)出返回JNI_VERSION_1_4的JNI_OnLoad函數(shù)。

如果本機(jī)庫不導(dǎo)出JNI_OnLoad函數(shù),則虛擬機(jī)假定庫僅需要JNI版本JNI_VERSION_1_1。如果VM無法識別JNI_OnLoad返回的版本號,則無法加載本機(jī)庫。

JNI_OnUnload

void JNI_OnUnload(JavaVM * vm,void * reserved);
當(dāng)包含本地庫的類加載器被垃圾回收時(shí),VM調(diào)用JNI_OnUnload。此功能可用于執(zhí)行清理操作。因?yàn)檫@個(gè)函數(shù)在一個(gè)未知的上下文(例如從一個(gè)finalizer)中被調(diào)用,所以程序員在使用Java VM服務(wù)時(shí)應(yīng)該是保守的,并且避免任意的Java回調(diào)。
請注意,JNI_OnLoad和JNI_OnUnload是JNI庫可選提供的兩個(gè)函數(shù),不是從VM導(dǎo)出的。

聯(lián)系:
從包含本機(jī)方法實(shí)現(xiàn)的本機(jī)庫導(dǎo)出。

調(diào)用API函數(shù)

JavaVM類型是一個(gè)指向Invocation API函數(shù)表的指針。以下代碼示例顯示此功能表。

typedef const struct JNIInvokeInterface * JavaVM;


const struct JNIInvokeInterface ... = {
    空值,
    空值,
    空值,
 
    DestroyJavaVM,
    AttachCurrentThread,
    DetachCurrentThread,

    GETENV,

    AttachCurrentThreadAsDaemon
};

請注意,三個(gè)Invocation API函數(shù)JNI_GetDefaultJavaVMInitArgs(),
JNI_GetCreatedJavaVMs()和JNI_CreateJavaVM()不是JavaVM功能表的一部分。這些功能可以在沒有預(yù)先存在的JavaVM結(jié)構(gòu)的情況下使用。
JNI_GetDefaultJavaVMInitArgs
jint JNI_GetDefaultJavaVMInitArgs(void * vm_args);返回Java VM的默認(rèn)配置。在調(diào)用此函數(shù)之前,本機(jī)代碼must1將vm_args-> version字段設(shè)置為它希望VM支持的JNI版本。在JDK 1.1.2中,vm_args->版本必須設(shè)置為0x00010001。此函數(shù)返回后,vm_args->版本將被設(shè)置為VM支持的實(shí)際JNI版本。

聯(lián)系:
從實(shí)現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出。

參數(shù):
vm_args:指向VM特定的初始化結(jié)構(gòu)的指針,默認(rèn)參數(shù)被填充到該初始化結(jié)構(gòu)中。

返回值:
如果支持請求的版本,返回“0”如果不支持請求的版本,則返回一個(gè)負(fù)數(shù)。

JNI_GetCreatedJavaVMs

jint JNI_GetCreatedJavaVMs(JavaVM ** vmBuf,jsize bufLen,jsize * nVMs);返回已創(chuàng)建的所有Java VM。指向VM的指針按照創(chuàng)建的順序?qū)懭刖彌_區(qū)vmBuf。最多寫入數(shù)量的條目將被寫入。創(chuàng)建的VM的總數(shù)在* nVMs中返回。

JDK 1.1.2不支持在單個(gè)進(jìn)程中創(chuàng)建多個(gè)VM。

聯(lián)系:
從實(shí)現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出。

參數(shù):
vmBuf:指向?qū)⒁胖肰M結(jié)構(gòu)的緩沖區(qū)的指針。
bufLen:緩沖區(qū)的長度。
nVMs:指向整數(shù)的指針。

返回值:
成功返回“0”在失敗時(shí)返回負(fù)數(shù)。

JNI_CreateJavaVM

jint JNI_CreateJavaVM(JavaVM ** p_vm,JNIEnv ** p_env,void * vm_args);加載并初始化Java VM。當(dāng)前線程成為主線程。將env參數(shù)設(shè)置為主線程的JNI接口指針。

JDK 1.1不支持在單個(gè)進(jìn)程中創(chuàng)建多個(gè)虛擬機(jī)。 vm_args中的version字段必須設(shè)置為0x00010001。

在JDK 1.1中,JNI_CreateJavaVM的第二個(gè)參數(shù)始終是指向JNIEnv *的指針。第三個(gè)參數(shù)是指向JDK 1.1特定結(jié)構(gòu)(JDK1_1InitArgs)的指針。JDK1_1InitArgs結(jié)構(gòu)顯然不是設(shè)計(jì)為在所有VM上可移植。

在JDK中,我們引入了一個(gè)標(biāo)準(zhǔn)的VM初始化結(jié)構(gòu)。向后兼容性保留。如果VM初始化參數(shù)指向JDK1_1InitArgs結(jié)構(gòu),則JNI_CreateJavaVM仍返回1.1版本的JNI接口指針。如果第三個(gè)參數(shù)指向JavaVMInitArgs結(jié)構(gòu),VM將返回1.2版本的JNI接口指針。與包含一組固定選項(xiàng)的JDK1_1InitArgs不同,JavaVMInitArgs使用選項(xiàng)字符串來編碼任意VM啟動選項(xiàng)。

typedef struct JavaVMInitArgs {
    jint version;

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

版本字段必須設(shè)置為JNI_VERSION_1_2。 (相反,JDK1_1InitArgs中的版本字段必須設(shè)置為JNI_VERSION_1_1。)options字段是以下類型的數(shù)組:

typedef struct JavaVMOption {
    char * optionString; / *該選項(xiàng)作為字符串在默認(rèn)平臺編碼* /
    void * extraInfo;
} JavaVMOption;

數(shù)組的大小由JavaVMInitArgs中的nOptions字段表示。如果ignoreUnrecognized是JNI_TRUE,JNI_CreateJavaVM將忽略以“-X”或“_”開頭的所有無法識別的選項(xiàng)字符串。如果ignoreUnrecognized是JNI_FALSE,JNI_CreateJavaVM在遇到任何無法識別的選項(xiàng)字符串時(shí)立即返回JNI_ERR。所有Java VM必須識別以下標(biāo)準(zhǔn)選項(xiàng)集:

Paste_Image.png
-D <name> = <value>設(shè)置系統(tǒng)屬性
-verbose [:class | gc | jni]啟用詳細(xì)輸出。這些選項(xiàng)之后可以用逗號分隔的名稱列表來指示VM將打印哪種類型的消息。例如,“-verbose:gc,class”指示VM打印GC和類加載相關(guān)的消息。標(biāo)準(zhǔn)名稱包括:gc,class和jni。所有非標(biāo)準(zhǔn)(VM特定)名稱必須以“X”開頭。
vfprintf extraInfo是指向vfprintf鉤子的指針。
exit extraInfo是一個(gè)指向退出鉤子的指針。
abort extraInfo是指向中止掛鉤的指針。

此外,每個(gè)VM實(shí)現(xiàn)可以支持其自己的一組非標(biāo)準(zhǔn)選項(xiàng)字符串。非標(biāo)準(zhǔn)選項(xiàng)名稱必須以“-X”或下劃線(“_”)開頭。例如,JDK支持-Xms和-Xmx選項(xiàng),以允許程序員指定初始和最大堆大小。從“-X”開始的選項(xiàng)可以從“java”命令行訪問。

以下是在JDK中創(chuàng)建Java VM的示例代碼:

JavaVMInitArgs vm_args;
JavaVMOption選項(xiàng)[4];

options [0] .optionString =“-Djava.compiler = NONE”; / *禁用JIT * /
options [1] .optionString =“-Djava.class.path = c:\ myclasses”; / *用戶類* /
options [2] .optionString =“-Djava.library.path = c:\ mylibs”; / *設(shè)置本機(jī)庫路徑* /
options [3] .optionString =“-verbose:jni”; / *打印與JNI相關(guān)的消息* /

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

/ *請注意,在JDK中,不再需要調(diào)用
 * JNI_GetDefaultJavaVMInitArgs。
 * /
res = JNI_CreateJavaVM(&vm,(void **)&env,&vm_args);
if(res <0)...

JDK仍然支持與JDK 1.1完全相同的JDK1_1InitArgs。

聯(lián)系:
從實(shí)現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出。

參數(shù):
p_vm:指向生成的VM結(jié)構(gòu)的位置的指針。
p_env:指向主線程的JNI接口指針的位置的指針。
vm_args:Java VM初始化參數(shù)。

返回值:
成功返回“0”在失敗時(shí)返回負(fù)數(shù)。

DestroyJavaVM

jint DestroyJavaVM(JavaVM * vm);卸載Java VM并收回資源。只有主線程可以卸載虛擬機(jī)。系統(tǒng)等待,直到主線程在其銷毀虛擬機(jī)之前只剩下用戶線程。

對DestroyJavaVM的支持在1.1中尚未完成。只有主線程可以調(diào)用DestroyJavaVM。在JDK中,任何線程(無論是否附加)都可以調(diào)用此函數(shù)。如果連接當(dāng)前線程,則VM等待直到當(dāng)前線程是唯一的用戶級Java線程。如果當(dāng)前線程未附加,則VM附加當(dāng)前線程,然后等待直到當(dāng)前線程為唯一的用戶級線程。然而,JDK仍然不支持VM卸載。 DestroyJavaVM總是返回錯(cuò)誤代碼。

聯(lián)系:
索引3在JavaVM界面的功能表中。

參數(shù):
vm:將被銷毀的Java VM。

返回值:
成功返回“0”在失敗時(shí)返回負(fù)數(shù)。

JDK 1.1.2不支持卸載VM。

AttachCurrentThread

jint AttachCurrentThread(JavaVM * vm,JNIEnv ** p_env,void * thr_args);將當(dāng)前線程附加到Java VM。返回JNIEnv參數(shù)中的JNI接口指針。

嘗試附加已經(jīng)附加的線程是無操作的。

本機(jī)線程不能同時(shí)連接到兩個(gè)Java VM。

當(dāng)線程附加到VM時(shí),上下文類加載器是引導(dǎo)加載程序。

聯(lián)系:
索引4在JavaVM界面功能表中。

參數(shù):
vm:當(dāng)前線程將附加到的VM。
p_env:指向當(dāng)前線程的JNI接口指針的位置的指針。
thr_args:VM特定的線程附件參數(shù)。

在JDK 1.1中,AttachCurrentThread的第二個(gè)參數(shù)總是指向JNIEnv的指針。
AttachCurrentThread的第三個(gè)參數(shù)被保留,并應(yīng)設(shè)置為NULL。

在JDK中,作為1.1行為的第三個(gè)參數(shù)傳遞NULL,或者將指針傳遞給以下結(jié)構(gòu)以指定其他信息:

typedef struct JavaVMAttachArgs {
    jint version;  /* must be 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

返回值:
成功返回“0”在失敗時(shí)返回負(fù)數(shù)。

AttachCurrentThreadAsDaemon

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

與AttachCurrentThread相同的語義,但新創(chuàng)建的java.lang.Thread實(shí)例是一個(gè)守護(hù)進(jìn)程。

如果線程已經(jīng)通過AttachCurrentThread或AttachCurrentThreadAsDaemon連接,則此例程將簡單地將penv指向的值設(shè)置為當(dāng)前線程的JNIEnv。在這種情況下,AttachCurrentThread和此例程都不會對線程的守護(hù)進(jìn)程狀態(tài)產(chǎn)生任何影響。

聯(lián)系:
索引7在JavaVM界面功能表中。

參數(shù):
vm:當(dāng)前線程將附加到的虛擬機(jī)實(shí)例。
penv:指向當(dāng)前線程的JNIEnv接口指針的位置的指針。
args:指向JavaVMAttachArgs結(jié)構(gòu)的指針。

返回值:
成功返回零;否則返回一個(gè)負(fù)數(shù)。

異常:
沒有。

SINCE:

JDK/JRE 1.4

DetachCurrentThread

jint DetachCurrentThread(JavaVM * vm);

從Java VM分離當(dāng)前線程。此線程持有的所有Java監(jiān)視器都將被釋放。通知所有等待此線程死機(jī)的Java線程。

在JDK 1.1中,主線程不能與VM分離。它必須調(diào)用DestroyJavaVM來卸載整個(gè)VM。

在JDK中,主線程可以與VM分離。

主線程(即創(chuàng)建Java VM的線程)無法從VM分離。相反,主線程必須調(diào)用JNI_DestroyJavaVM()來卸載整個(gè)VM。

聯(lián)系:
JavaVM界面功能表中的索引5。

參數(shù):
vm:當(dāng)前線程將從其中分離的VM。

返回值:
成功返回“0”在失敗時(shí)返回負(fù)數(shù)。

GETENV

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

聯(lián)系:
索引6在JavaVM界面功能表中。

返回值:
如果當(dāng)前線程未連接到VM,則將* env設(shè)置為NULL,并返回JNI_EDETACHED。如果不支持指定的版本,將* env設(shè)置為NULL,并返回JNI_EVERSION。否則,將* env設(shè)置到適當(dāng)?shù)慕涌?,并返回JNI_OK。

SINCE:

JDK / JRE 1.2


  1. JDK 1.1不需要本地代碼設(shè)置版本字段。為了向后兼容,如果版本字段未設(shè)置,JDK 1.1.2假定

所請求的版本為0x00010001。 JDK的未來版本將要求將版本字段設(shè)置為適當(dāng)?shù)闹怠?/p>

2.見腳注1。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容