Java 本地接口規(guī)范
發(fā)表時(shí)間:2024-01-15 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]本章介紹 Java 本地接口(Java Native Interface,JNI)。JNI 是本地編程接口。它使得在 Java 虛擬機(jī) (VM) 內(nèi)部運(yùn)行的 Java 代碼能夠與用其它編程語(yǔ)言(如 C、C++ 和匯編語(yǔ)言)編寫(xiě)的應(yīng)用程序和庫(kù)進(jìn)行互操作! JNI 最重要的好處是它沒(méi)有對(duì)底層...
本章介紹 Java 本地接口(Java Native Interface,JNI)。JNI 是本地編程接口。它使得在 Java 虛擬機(jī) (VM) 內(nèi)部運(yùn)行的 Java 代碼能夠與用其它編程語(yǔ)言(如 C、C++ 和匯編語(yǔ)言)編寫(xiě)的應(yīng)用程序和庫(kù)進(jìn)行互操作。
JNI 最重要的好處是它沒(méi)有對(duì)底層 Java 虛擬機(jī)的實(shí)現(xiàn)施加任何限制。因此,Java 虛擬機(jī)廠商可以在不影響虛擬機(jī)其它部分的情況下添加對(duì) JNI 的支持。程序員只需編寫(xiě)一種版本的本地應(yīng)用程序或庫(kù),就能夠與所有支持 JNI 的 Java 虛擬機(jī)協(xié)同工作。
本章論及以下主題:
Java 本地接口概述
背景
目標(biāo)
Java 本地接口方法
利用 JNI 編程
JDK 1.1.2 中的變化
--------------------------------------------------------------------------------
Java 本地接口概述
盡管可以完全用 Java 編寫(xiě)應(yīng)用程序,但是有時(shí)單獨(dú)用 Java 不能滿(mǎn)足應(yīng)用程序的需要。程序員使用 JNI 來(lái)編寫(xiě) Java 本地方法,可以處理那些不能完全用 Java 編寫(xiě)應(yīng)用程序的情況。
以下示例說(shuō)明了何時(shí)需要使用 Java 本地方法:
標(biāo)準(zhǔn) Java 類(lèi)庫(kù)不支持與平臺(tái)相關(guān)的應(yīng)用程序所需的功能。
已經(jīng)擁有了一個(gè)用另一種語(yǔ)言編寫(xiě)的庫(kù),而又希望通過(guò) JNI 使 Java 代碼能夠訪問(wèn)該庫(kù)。
想用低級(jí)語(yǔ)言(如匯編語(yǔ)言)實(shí)現(xiàn)一小段時(shí)限代碼。
通過(guò)用 JNI 編程,可以將本地方法用于:
創(chuàng)建、檢查及更新 Java 對(duì)象(包括數(shù)組和字符串)。
調(diào)用 Java 方法。
捕捉和拋出異常。
加載類(lèi)和獲得類(lèi)信息。
執(zhí)行運(yùn)行時(shí)類(lèi)型檢查。
也可以與調(diào)用 API 一起使用 JNI,以允許任意本地應(yīng)用程序嵌入到 Java 虛擬機(jī)中。這樣使得程序員能夠輕易地讓已有應(yīng)用程序支持 Java,而不必與虛擬機(jī)源代碼相鏈接。
--------------------------------------------------------------------------------
背景
目前,不同廠商的虛擬機(jī)提供了不同的本地方法接口。這些不同的接口使程序員不得不在給定平臺(tái)上編寫(xiě)、維護(hù)和分發(fā)多種版本的本地方法庫(kù)。
下面簡(jiǎn)要分析一下部分已有本地方法接口,例如:
JDK 1.0 本地方法接口
Netscape 的 Java 運(yùn)行時(shí)接口
Microsoft 的原始本地接口和 Java/COM 接口
JDK 1.0 本地方法接口
JDK 1.0 附帶有本地方法接口。遺憾的是,有兩點(diǎn)原因使得該接口不適合于其它 Java 虛擬機(jī)。
第一,平臺(tái)相關(guān)代碼將 Java 對(duì)象中的域作為 C 結(jié)構(gòu)的成員來(lái)進(jìn)行訪問(wèn)。但是,Java 語(yǔ)言規(guī)范沒(méi)有規(guī)定在內(nèi)存中對(duì)象是如何布局的。如果 Java 虛擬機(jī)在內(nèi)存中布局對(duì)象的方式有所不同,程序員就不得不重新編譯本地方法庫(kù)。
第二,JDK 1.0 的本地方法接口依賴(lài)于保守的垃圾收集器。例如,無(wú)限制地使用 unhand 宏使得有必要以保守方式掃描本地堆棧。
Java 運(yùn)行時(shí)接口
Netscape 建議使用 Java 運(yùn)行時(shí)接口 (JRI),它是 Java 虛擬機(jī)所提供服務(wù)的通用接口。JRI 的設(shè)計(jì)融入了可移植性---它幾乎沒(méi)有對(duì)底層 Java 虛擬機(jī)的實(shí)現(xiàn)細(xì)節(jié)作任何假設(shè)。JRI 提出了各種各樣的問(wèn)題,包括本地方法、調(diào)試、反射、嵌入(調(diào)用)等等。
原始本地接口和 Java/COM 接口
Microsoft Java 虛擬機(jī)支持兩種本地方法接口。在低一級(jí),它提供了高效的原始本地接口 (RNI)。RNI 提供了與 JDK 本地方法接口有高度源代碼級(jí)的向后兼容性,盡管它們之間還有一個(gè)主要區(qū)別,即平臺(tái)相關(guān)代碼必須用 RNI 函數(shù)來(lái)與垃圾收集器進(jìn)行顯式的交互,而不是依賴(lài)于保守的垃圾收集。
在高一級(jí),Microsoft 的 Java/COM 接口為 Java 虛擬機(jī)提供了與語(yǔ)言無(wú)關(guān)的標(biāo)準(zhǔn)二進(jìn)制接口。Java 代碼可以象使用 Java 對(duì)象一樣來(lái)使用 COM 對(duì)象。Java 類(lèi)也可以作為 COM 類(lèi)顯示給系統(tǒng)的其余部分。
--------------------------------------------------------------------------------
目標(biāo)
我們認(rèn)為統(tǒng)一的,經(jīng)過(guò)細(xì)致考慮的標(biāo)準(zhǔn)接口能夠向每個(gè)用戶(hù)提供以下好處:
每個(gè)虛擬機(jī)廠商都可以支持更多的平臺(tái)相關(guān)代碼。
工具構(gòu)造器不必維護(hù)不同的本地方法接口。
應(yīng)用程序設(shè)計(jì)人員可以只編寫(xiě)一種版本的平臺(tái)相關(guān)代碼就能夠在不同的虛擬機(jī)上運(yùn)行。
獲得標(biāo)準(zhǔn)本地方法接口的最佳途徑是聯(lián)合所有對(duì) Java 虛擬機(jī)有興趣的當(dāng)事方。因此,我們?cè)?Java 獲得許可方之間組織了一系列研討會(huì),對(duì)設(shè)計(jì)統(tǒng)一的本地方法接口進(jìn)行了討論。從研討會(huì)可以明確地看出標(biāo)準(zhǔn)本地方法接口必須滿(mǎn)足以下要求:
二進(jìn)制兼容性 - 主要的目標(biāo)是在給定平臺(tái)上的所有 Java 虛擬機(jī)實(shí)現(xiàn)之間實(shí)現(xiàn)本地方法庫(kù)的二進(jìn)制兼容性。對(duì)于給定平臺(tái),程序員只需要維護(hù)一種版本的本地方法庫(kù)。
效率 - 若要支持時(shí)限代碼,本地方法接口必須增加一點(diǎn)系統(tǒng)開(kāi)銷(xiāo)。所有已知的用于確保虛擬機(jī)無(wú)關(guān)性(因而具有二進(jìn)制兼容性)的技術(shù)都會(huì)占用一定的系統(tǒng)開(kāi)銷(xiāo)。我們必須在效率與虛擬機(jī)無(wú)關(guān)性之間進(jìn)行某種折衷。
功能 - 接口必須顯示足夠的 Java 虛擬機(jī)內(nèi)部情況以使本地方法能夠完成有用的任務(wù)。
--------------------------------------------------------------------------------
Java 本地接口方法
我們希望采用一種已有的方法作為標(biāo)準(zhǔn)接口,因?yàn)檫@樣程序員(程序員不得不學(xué)習(xí)在不同虛擬機(jī)中的多種接口)的工作負(fù)擔(dān)最輕。遺憾的是,已有解決方案中沒(méi)有任何方案能夠完全地滿(mǎn)足我們的目標(biāo)。
Netscape 的 JRI 最接近于我們所設(shè)想的可移植本地方法接口,因而我們采用它作為設(shè)計(jì)起點(diǎn)。熟悉 JRI 的讀者將會(huì)注意到在 API 命名規(guī)則、方法和域 ID 的使用、局部和全局引用的使用,等等中的相似點(diǎn)。雖然我們進(jìn)行了最大的努力,但是 JNI 并不具有對(duì) JRI 的二進(jìn)制兼容性,不過(guò)虛擬機(jī)既可以支持 JRI,又可以支持 JNI。
Microsoft 的 RNI 是對(duì) JDK 1.0 的改進(jìn),因?yàn)樗梢越鉀Q使用非保守的垃圾收集器的本地方法的問(wèn)題。然而,RNI 不適合用作與虛擬機(jī)無(wú)關(guān)的本地方法接口。與 JDK 類(lèi)似,RNI 本地方法將 Java 對(duì)象作為 C 結(jié)構(gòu)來(lái)訪問(wèn)。這將導(dǎo)致兩個(gè)問(wèn)題:
RNI 將內(nèi)部 Java 對(duì)象的布局暴露給了平臺(tái)相關(guān)代碼。
將 Java 對(duì)象作為 C 結(jié)構(gòu)直接進(jìn)行訪問(wèn)使得不可能有效地加入“寫(xiě)屏障”,寫(xiě)屏障是高級(jí)的垃圾收集算法所必需的。
作為二進(jìn)制標(biāo)準(zhǔn),COM 確保了不同虛擬機(jī)之間的完全二進(jìn)制兼容性。調(diào)用 COM 方法只要求間接調(diào)用,而這幾乎不會(huì)占用系統(tǒng)開(kāi)銷(xiāo)。另外,COM 對(duì)象對(duì)動(dòng)態(tài)鏈接庫(kù)解決版本問(wèn)題的方式也有很大的改進(jìn)。
然而,有幾個(gè)因素阻礙了將 COM 用作標(biāo)準(zhǔn) Java 本地方法接口:
第一,Java/COM 接口缺少某些必需功能,例如訪問(wèn)私有域和拋出普通異常。
第二,Java/COM 接口自動(dòng)為 Java 對(duì)象提供標(biāo)準(zhǔn)的 IUnknown 和 IDispatch COM 接口,因而平臺(tái)相關(guān)代碼能夠訪問(wèn)公有方法和域。遺憾的是,IDispatch 接口不能處理重載的 Java 方法,而且在匹配方法名稱(chēng)時(shí)不區(qū)別大小寫(xiě)。另外,通過(guò) IDispatch 接口暴露的所有 Java 方法被打包在一起來(lái)執(zhí)行動(dòng)態(tài)類(lèi)型檢查和強(qiáng)制轉(zhuǎn)換。這是因?yàn)?IDispatch 接口的設(shè)計(jì)只考慮到了弱類(lèi)型的語(yǔ)言(例如 Basic)。
第三,COM 允許軟件組件(包括完全成熟的應(yīng)用程序)一起工作,而不是處理單個(gè)低層函數(shù)。我們認(rèn)為將所有 Java 類(lèi)或低層本地方法都當(dāng)作軟件組件是不恰當(dāng)?shù)摹?br>
第四,在 UNIX 平臺(tái)上由于缺少對(duì) COM 的支持,所以阻礙了直接采用 COM。
雖然我們沒(méi)有將 Java 對(duì)象作為 COM 對(duì)象暴露給平臺(tái)相關(guān)代碼,但是 JNI 接口自身與 COM 具有二進(jìn)制兼容性。我們采用與 COM 一樣的跳轉(zhuǎn)表和調(diào)用約定。這意味著,一旦具有對(duì) COM 的跨平臺(tái)支持,JNI 就能成為 Java 虛擬機(jī)的 COM 接口。
我們認(rèn)為 JNI 不應(yīng)該是給定 Java 虛擬機(jī)所支持的唯一的本地方法接口。標(biāo)準(zhǔn)接口的好處在于程序員可以將自己的平臺(tái)相關(guān)代碼庫(kù)加載到不同的 Java 虛擬機(jī)上。在某些情況下,程序員可能不得不使用低層且與虛擬機(jī)有關(guān)的接口來(lái)獲得較高的效率。但在其它情況下,程序員可能使用高層接口來(lái)建立軟件組件。實(shí)際上,我們希望隨著 Java 環(huán)境和組件軟件技術(shù)發(fā)展得越來(lái)越成熟,本地方法將變得越來(lái)越不重要。
--------------------------------------------------------------------------------
利用 JNI 編程
本地方法程序設(shè)計(jì)人員應(yīng)開(kāi)始利用 JNI 進(jìn)行編程。利用 JNI 編程隔離了一些未知條件,例如終端用戶(hù)可能正在運(yùn)行的廠商的虛擬機(jī)。遵守 JNI 標(biāo)準(zhǔn)是本地庫(kù)能在給定 Java 虛擬機(jī)上運(yùn)行的最好保證。例如,雖然 JDK 1.1 將繼續(xù)支持 JDK 1.0 中所實(shí)現(xiàn)的舊式的本地方法接口,但是可以肯定的是 JDK 的未來(lái)版本將停止支持舊式的本地方法接口。依賴(lài)于舊式接口的本地方法將不得不重新編寫(xiě)。
如果您正在實(shí)現(xiàn) Java 虛擬機(jī),則應(yīng)該實(shí)現(xiàn) JNI。我們(Javasoft 和獲得許可方)盡力確保 JNI 不會(huì)占用虛擬機(jī)實(shí)現(xiàn)的系統(tǒng)開(kāi)銷(xiāo)或施加任何限制,包括對(duì)象表示,垃圾收集機(jī)制等。如果您遇到了我們可能忽視了的問(wèn)題,請(qǐng)告知我們。
--------------------------------------------------------------------------------
JDK 1.1.2 中的變化
為了更好地支持 Java 運(yùn)行時(shí)環(huán)境 (JRE),在 JDK 1.1.2 中對(duì)調(diào)用 API 在幾個(gè)方面作了擴(kuò)展。這些變化沒(méi)有破壞任何已有代碼,JNI 本地方法接口也沒(méi)有改變。
JDK1_1InitArgs 結(jié)構(gòu)中的 reserved0 域已被重新命名為 version。JDK1_1InitArgs 結(jié)構(gòu)保存 JNI_CreateJavaVM 的初始化參數(shù)。JNI_GetDefaultJavaVMInitArgs 和 JNI_CreateJavaVM 的調(diào)用者必須將版本域設(shè)置為 0x00010001。JNI_GetDefaultJavaVMInitArgs 被更改為返回 jint,用于表示是否支持所請(qǐng)求的版本。
JDK1_1InitArgs 結(jié)構(gòu)中的 reserved1 域已被重新命名為 properties。這是一個(gè) NULL-終結(jié)的字符串?dāng)?shù)組。每個(gè)字符串具有以下格式:
name=value
表示系統(tǒng)屬性(該功能對(duì)應(yīng)于 Java 命令行中的 -D 選項(xiàng))。
在 JDK 1.1.1 中,調(diào)用 DestroyJavaVM 的線(xiàn)程必須是虛擬機(jī)中的唯一用戶(hù)線(xiàn)程。JDK 1.1.2 放松了這一限制。如果調(diào)用 DestroyJavaVM 時(shí)有多個(gè)用戶(hù)線(xiàn)程,則虛擬機(jī)將等待直到當(dāng)前線(xiàn)程成為唯一的用戶(hù)線(xiàn)程,然后銷(xiāo)毀自己。