譯文地址:
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ù)。在這個例子中,C ++代碼創(chuàng)建一個Java VM并調(diào)用一個稱為Main.test的靜態(tài)方法砍濒。為了清楚起見,我們省略錯誤檢查硫麻。
#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爸邢,返回一個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中使用三個函數(shù)柳洋。 Invocation API允許本機(jī)應(yīng)用程序使用JNI接口指針來訪問VM功能待诅。該設(shè)計類似于Netscape的JRI嵌入式界面。
創(chuàng)建虛擬機(jī)
JNI_CreateJavaVM()函數(shù)加載并初始化Java VM熊镣,并返回指向JNI接口指針的指針卑雁。調(diào)用JNI_CreateJavaVM()的線程被認(rèn)為是主線程立由。
連接到虛擬機(jī)
JNI接口指針(JNIEnv)僅在當(dāng)前線程中有效。如果另一個線程需要訪問Java VM序厉,則必須首先調(diào)用AttachCurrentThread()將其自身附加到VM并獲取JNI接口指針锐膜。一旦連接到VM,本機(jī)線程就像在本機(jī)方法中運行的普通Java線程一樣工作弛房。本機(jī)線程仍然連接到VM道盏,直到它調(diào)用DetachCurrentThread()來自行分離。
附加的線程應(yīng)該有足夠的堆椢拇罚空間來執(zhí)行可重復(fù)的工作量荷逞。每個線程的堆棧空間分配是操作系統(tǒng)特定的粹排。例如种远,使用pthreads,可以在pthread_attr_t參數(shù)中指定堆棧大小為pthread_create顽耳。
卸載虛擬機(jī)
主線程無法從虛擬機(jī)分離坠敷。相反,它必須調(diào)用DestroyJavaVM()來卸載整個VM射富。
VM等待直到主線程在實際卸載之前是唯一的用戶線程膝迎。用戶線程包括Java線程和附加的本機(jī)線程。存在此限制胰耗,因為Java線程或附加的本機(jī)線程可能正在保存系統(tǒng)資源限次,例如鎖,窗口等柴灯。虛擬機(jī)無法自動釋放這些資源卖漫。通過將主線程限制為VM卸載時唯一運行的線程,釋放由任意線程保存的系統(tǒng)資源的負(fù)擔(dān)在程序員身上赠群。
<h1>庫和版本管理<h1>
在JDK 1.1中羊始,一旦加載本地庫,它就可以從所有的類加載器中看到乎串。因此店枣,不同類加載器中的兩個類可以與相同的本機(jī)方法鏈接。這會導(dǎo)致兩個問題:
類可能會錯誤地鏈接到由不同類加載器中具有相同名稱的類加載的本機(jī)庫叹誉。
本機(jī)方法可以輕松地從不同的類加載器混合類鸯两。這打破了類裝載機(jī)提供的名稱空間分離,并導(dǎo)致類型安全問題长豁。
在JDK中钧唐,每個類加載器都管理自己的一組本機(jī)庫。相同的JNI本地庫不能加載到多個類加載器中匠襟。這樣做會引起“不滿意的鏈接錯誤”钝侠。例如该园,System.loadLibrary在將本機(jī)庫加載到兩個加載器中時拋出一個UnsatisfiedLinkError。新方法的好處是:基于類加載器的名稱空間分隔保留在本機(jī)庫中帅韧。一個本機(jī)庫不能輕易地混合來自不同類加載器的類里初。
另外,本地庫可以在相應(yīng)的類加載器被垃圾回收時卸載忽舟。
為了方便版本控制和資源管理双妨,Java 2 Platform中的JNI庫可以選擇導(dǎo)出以下兩個功能:
JNI_OnLoad
--
jint JNI_OnLoad(JavaVM * vm,void * reserved);
加載本地庫時叮阅,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ī)方法實現(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)包含本地庫的類加載器被垃圾回收時,VM調(diào)用JNI_OnUnload该溯。此功能可用于執(zhí)行清理操作岛抄。因為這個函數(shù)在一個未知的上下文(例如從一個finalizer)中被調(diào)用,所以程序員在使用Java VM服務(wù)時應(yīng)該是保守的狈茉,并且避免任意的Java回調(diào)夫椭。
請注意,JNI_OnLoad和JNI_OnUnload是JNI庫可選提供的兩個函數(shù)氯庆,不是從VM導(dǎo)出的蹭秋。
聯(lián)系:
從包含本機(jī)方法實現(xiàn)的本機(jī)庫導(dǎo)出扰付。
調(diào)用API函數(shù)
JavaVM類型是一個指向Invocation API函數(shù)表的指針。以下代碼示例顯示此功能表仁讨。
typedef const struct JNIInvokeInterface * JavaVM;
const struct JNIInvokeInterface ... = {
空值羽莺,
空值,
空值洞豁,
DestroyJavaVM盐固,
AttachCurrentThread,
DetachCurrentThread族跛,
GETENV闰挡,
AttachCurrentThreadAsDaemon
};
請注意,三個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支持的實際JNI版本烁竭。
聯(lián)系:
從實現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出菲茬。
參數(shù):
vm_args:指向VM特定的初始化結(jié)構(gòu)的指針,默認(rèn)參數(shù)被填充到該初始化結(jié)構(gòu)中派撕。
返回值:
如果支持請求的版本婉弹,返回“0”如果不支持請求的版本,則返回一個負(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不支持在單個進(jìn)程中創(chuàng)建多個VM良姆。
聯(lián)系:
從實現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出。
參數(shù):
vmBuf:指向?qū)⒁胖肰M結(jié)構(gòu)的緩沖區(qū)的指針穴肘。
bufLen:緩沖區(qū)的長度歇盼。
nVMs:指向整數(shù)的指針。
返回值:
成功返回“0”在失敗時返回負(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不支持在單個進(jìn)程中創(chuàng)建多個虛擬機(jī)。 vm_args中的version字段必須設(shè)置為0x00010001氮惯。
在JDK 1.1中叮雳,JNI_CreateJavaVM的第二個參數(shù)始終是指向JNIEnv *的指針。第三個參數(shù)是指向JDK 1.1特定結(jié)構(gòu)(JDK1_1InitArgs)的指針妇汗。JDK1_1InitArgs結(jié)構(gòu)顯然不是設(shè)計為在所有VM上可移植帘不。
在JDK中,我們引入了一個標(biāo)準(zhǔn)的VM初始化結(jié)構(gòu)杨箭。向后兼容性保留寞焙。如果VM初始化參數(shù)指向JDK1_1InitArgs結(jié)構(gòu),則JNI_CreateJavaVM仍返回1.1版本的JNI接口指針互婿。如果第三個參數(shù)指向JavaVMInitArgs結(jié)構(gòu)捣郊,VM將返回1.2版本的JNI接口指針。與包含一組固定選項的JDK1_1InitArgs不同慈参,JavaVMInitArgs使用選項字符串來編碼任意VM啟動選項呛牲。
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; / *該選項作為字符串在默認(rèn)平臺編碼* /
void * extraInfo;
} JavaVMOption;
數(shù)組的大小由JavaVMInitArgs中的nOptions字段表示。如果ignoreUnrecognized是JNI_TRUE壮锻,JNI_CreateJavaVM將忽略以“-X”或“_”開頭的所有無法識別的選項字符串畜侦。如果ignoreUnrecognized是JNI_FALSE,JNI_CreateJavaVM在遇到任何無法識別的選項字符串時立即返回JNI_ERR躯保。所有Java VM必須識別以下標(biāo)準(zhǔn)選項集:
-D <name> = <value>設(shè)置系統(tǒng)屬性
-verbose [:class | gc | jni]啟用詳細(xì)輸出。這些選項之后可以用逗號分隔的名稱列表來指示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是一個指向退出鉤子的指針。
abort extraInfo是指向中止掛鉤的指針娃承。
此外奏夫,每個VM實現(xiàn)可以支持其自己的一組非標(biāo)準(zhǔn)選項字符串怕篷。非標(biāo)準(zhǔn)選項名稱必須以“-X”或下劃線(“_”)開頭。例如酗昼,JDK支持-Xms和-Xmx選項廊谓,以允許程序員指定初始和最大堆大小。從“-X”開始的選項可以從“java”命令行訪問麻削。
以下是在JDK中創(chuàng)建Java VM的示例代碼:
JavaVMInitArgs vm_args;
JavaVMOption選項[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)系:
從實現(xiàn)Java虛擬機(jī)的本機(jī)庫導(dǎo)出榛鼎。
參數(shù):
p_vm:指向生成的VM結(jié)構(gòu)的位置的指針。
p_env:指向主線程的JNI接口指針的位置的指針公给。
vm_args:Java VM初始化參數(shù)借帘。
返回值:
成功返回“0”在失敗時返回負(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總是返回錯誤代碼饲嗽。
聯(lián)系:
索引3在JavaVM界面的功能表中炭玫。
參數(shù):
vm:將被銷毀的Java VM。
返回值:
成功返回“0”在失敗時返回負(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ī)線程不能同時連接到兩個Java VM巫财。
當(dāng)線程附加到VM時盗似,上下文類加載器是引導(dǎo)加載程序。
聯(lián)系:
索引4在JavaVM界面功能表中平项。
參數(shù):
vm:當(dāng)前線程將附加到的VM赫舒。
p_env:指向當(dāng)前線程的JNI接口指針的位置的指針。
thr_args:VM特定的線程附件參數(shù)闽瓢。
在JDK 1.1中接癌,AttachCurrentThread的第二個參數(shù)總是指向JNIEnv的指針。
AttachCurrentThread的第三個參數(shù)被保留扣讼,并應(yīng)設(shè)置為NULL缺猛。
在JDK中,作為1.1行為的第三個參數(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”在失敗時返回負(fù)數(shù)荔燎。
AttachCurrentThreadAsDaemon
jint AttachCurrentThreadAsDaemon(JavaVM * vm,void ** penv销钝,void * args);
與AttachCurrentThread相同的語義有咨,但新創(chuàng)建的java.lang.Thread實例是一個守護(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ī)實例蜡吧。
penv:指向當(dāng)前線程的JNIEnv接口指針的位置的指針如迟。
args:指向JavaVMAttachArgs結(jié)構(gòu)的指針一也。
返回值:
成功返回零;否則返回一個負(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來卸載整個VM埂材。
在JDK中塑顺,主線程可以與VM分離。
主線程(即創(chuàng)建Java VM的線程)無法從VM分離。相反严拒,主線程必須調(diào)用JNI_DestroyJavaVM()來卸載整個VM扬绪。
聯(lián)系:
JavaVM界面功能表中的索引5。
參數(shù):
vm:當(dāng)前線程將從其中分離的VM裤唠。
返回值:
成功返回“0”在失敗時返回負(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ù)慕涌诒醭梗⒎祷豃NI_OK藕届。
SINCE:
JDK / JRE 1.2
- JDK 1.1不需要本地代碼設(shè)置版本字段。為了向后兼容亭饵,如果版本字段未設(shè)置休偶,JDK 1.1.2假定
所請求的版本為0x00010001。 JDK的未來版本將要求將版本字段設(shè)置為適當(dāng)?shù)闹怠?/p>
2.見腳注1冬骚。