明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

Java 本地接口規(guī)范

[摘要]本章介紹 Java 本地接口(Java Native Interface,JNI)。JNI 是本地編程接口。它使得在 Java 虛擬機 (VM) 內(nèi)部運行的 Java 代碼能夠與用其它編程語言(如 C、C++ 和匯編語言)編寫的應用程序和庫進行互操作!  JNI 最重要的好處是它沒有對底層...
本章介紹 Java 本地接口(Java Native Interface,JNI)。JNI 是本地編程接口。它使得在 Java 虛擬機 (VM) 內(nèi)部運行的 Java 代碼能夠與用其它編程語言(如 C、C++ 和匯編語言)編寫的應用程序和庫進行互操作。

   JNI 最重要的好處是它沒有對底層 Java 虛擬機的實現(xiàn)施加任何限制。因此,Java 虛擬機廠商可以在不影響虛擬機其它部分的情況下添加對 JNI 的支持。程序員只需編寫一種版本的本地應用程序或庫,就能夠與所有支持 JNI 的 Java 虛擬機協(xié)同工作。

   本章論及以下主題:

   Java 本地接口概述
   背景
   目標
   Java 本地接口方法
   利用 JNI 編程
   JDK 1.1.2 中的變化
   --------------------------------------------------------------------------------
   Java 本地接口概述
   盡管可以完全用 Java 編寫應用程序,但是有時單獨用 Java 不能滿足應用程序的需要。程序員使用 JNI 來編寫 Java 本地方法,可以處理那些不能完全用 Java 編寫應用程序的情況。

   以下示例說明了何時需要使用 Java 本地方法:

   標準 Java 類庫不支持與平臺相關的應用程序所需的功能。

   已經(jīng)擁有了一個用另一種語言編寫的庫,而又希望通過 JNI 使 Java 代碼能夠訪問該庫。

   想用低級語言(如匯編語言)實現(xiàn)一小段時限代碼。

   通過用 JNI 編程,可以將本地方法用于:

   創(chuàng)建、檢查及更新 Java 對象(包括數(shù)組和字符串)。

   調(diào)用 Java 方法。

   捕捉和拋出異常。

   加載類和獲得類信息。

   執(zhí)行運行時類型檢查。

   也可以與調(diào)用 API 一起使用 JNI,以允許任意本地應用程序嵌入到 Java 虛擬機中。這樣使得程序員能夠輕易地讓已有應用程序支持 Java,而不必與虛擬機源代碼相鏈接。

   --------------------------------------------------------------------------------
   背景
   目前,不同廠商的虛擬機提供了不同的本地方法接口。這些不同的接口使程序員不得不在給定平臺上編寫、維護和分發(fā)多種版本的本地方法庫。

   下面簡要分析一下部分已有本地方法接口,例如:

   JDK 1.0 本地方法接口
   Netscape 的 Java 運行時接口
   Microsoft 的原始本地接口和 Java/COM 接口
   JDK 1.0 本地方法接口
   JDK 1.0 附帶有本地方法接口。遺憾的是,有兩點原因使得該接口不適合于其它 Java 虛擬機。

   第一,平臺相關代碼將 Java 對象中的域作為 C 結構的成員來進行訪問。但是,Java 語言規(guī)范沒有規(guī)定在內(nèi)存中對象是如何布局的。如果 Java 虛擬機在內(nèi)存中布局對象的方式有所不同,程序員就不得不重新編譯本地方法庫。

   第二,JDK 1.0 的本地方法接口依賴于保守的垃圾收集器。例如,無限制地使用 unhand 宏使得有必要以保守方式掃描本地堆棧。

   Java 運行時接口
   Netscape 建議使用 Java 運行時接口 (JRI),它是 Java 虛擬機所提供服務的通用接口。JRI 的設計融入了可移植性---它幾乎沒有對底層 Java 虛擬機的實現(xiàn)細節(jié)作任何假設。JRI 提出了各種各樣的問題,包括本地方法、調(diào)試、反射、嵌入(調(diào)用)等等。

   原始本地接口和 Java/COM 接口
   Microsoft Java 虛擬機支持兩種本地方法接口。在低一級,它提供了高效的原始本地接口 (RNI)。RNI 提供了與 JDK 本地方法接口有高度源代碼級的向后兼容性,盡管它們之間還有一個主要區(qū)別,即平臺相關代碼必須用 RNI 函數(shù)來與垃圾收集器進行顯式的交互,而不是依賴于保守的垃圾收集。

   在高一級,Microsoft 的 Java/COM 接口為 Java 虛擬機提供了與語言無關的標準二進制接口。Java 代碼可以象使用 Java 對象一樣來使用 COM 對象。Java 類也可以作為 COM 類顯示給系統(tǒng)的其余部分。

   --------------------------------------------------------------------------------
   目標
   我們認為統(tǒng)一的,經(jīng)過細致考慮的標準接口能夠向每個用戶提供以下好處:

   每個虛擬機廠商都可以支持更多的平臺相關代碼。

   工具構造器不必維護不同的本地方法接口。

   應用程序設計人員可以只編寫一種版本的平臺相關代碼就能夠在不同的虛擬機上運行。

   獲得標準本地方法接口的最佳途徑是聯(lián)合所有對 Java 虛擬機有興趣的當事方。因此,我們在 Java 獲得許可方之間組織了一系列研討會,對設計統(tǒng)一的本地方法接口進行了討論。從研討會可以明確地看出標準本地方法接口必須滿足以下要求:

   二進制兼容性 - 主要的目標是在給定平臺上的所有 Java 虛擬機實現(xiàn)之間實現(xiàn)本地方法庫的二進制兼容性。對于給定平臺,程序員只需要維護一種版本的本地方法庫。

   效率 - 若要支持時限代碼,本地方法接口必須增加一點系統(tǒng)開銷。所有已知的用于確保虛擬機無關性(因而具有二進制兼容性)的技術都會占用一定的系統(tǒng)開銷。我們必須在效率與虛擬機無關性之間進行某種折衷。

   功能 - 接口必須顯示足夠的 Java 虛擬機內(nèi)部情況以使本地方法能夠完成有用的任務。

   --------------------------------------------------------------------------------
   Java 本地接口方法
   我們希望采用一種已有的方法作為標準接口,因為這樣程序員(程序員不得不學習在不同虛擬機中的多種接口)的工作負擔最輕。遺憾的是,已有解決方案中沒有任何方案能夠完全地滿足我們的目標。

   Netscape 的 JRI 最接近于我們所設想的可移植本地方法接口,因而我們采用它作為設計起點。熟悉 JRI 的讀者將會注意到在 API 命名規(guī)則、方法和域 ID 的使用、局部和全局引用的使用,等等中的相似點。雖然我們進行了最大的努力,但是 JNI 并不具有對 JRI 的二進制兼容性,不過虛擬機既可以支持 JRI,又可以支持 JNI。

   Microsoft 的 RNI 是對 JDK 1.0 的改進,因為它可以解決使用非保守的垃圾收集器的本地方法的問題。然而,RNI 不適合用作與虛擬機無關的本地方法接口。與 JDK 類似,RNI 本地方法將 Java 對象作為 C 結構來訪問。這將導致兩個問題:

   RNI 將內(nèi)部 Java 對象的布局暴露給了平臺相關代碼。

   將 Java 對象作為 C 結構直接進行訪問使得不可能有效地加入“寫屏障”,寫屏障是高級的垃圾收集算法所必需的。

   作為二進制標準,COM 確保了不同虛擬機之間的完全二進制兼容性。調(diào)用 COM 方法只要求間接調(diào)用,而這幾乎不會占用系統(tǒng)開銷。另外,COM 對象對動態(tài)鏈接庫解決版本問題的方式也有很大的改進。

   然而,有幾個因素阻礙了將 COM 用作標準 Java 本地方法接口:

   第一,Java/COM 接口缺少某些必需功能,例如訪問私有域和拋出普通異常。

   第二,Java/COM 接口自動為 Java 對象提供標準的 IUnknown 和 IDispatch COM 接口,因而平臺相關代碼能夠訪問公有方法和域。遺憾的是,IDispatch 接口不能處理重載的 Java 方法,而且在匹配方法名稱時不區(qū)別大小寫。另外,通過 IDispatch 接口暴露的所有 Java 方法被打包在一起來執(zhí)行動態(tài)類型檢查和強制轉換。這是因為 IDispatch 接口的設計只考慮到了弱類型的語言(例如 Basic)。

   第三,COM 允許軟件組件(包括完全成熟的應用程序)一起工作,而不是處理單個低層函數(shù)。我們認為將所有 Java 類或低層本地方法都當作軟件組件是不恰當?shù)摹?br>
   第四,在 UNIX 平臺上由于缺少對 COM 的支持,所以阻礙了直接采用 COM。

   雖然我們沒有將 Java 對象作為 COM 對象暴露給平臺相關代碼,但是 JNI 接口自身與 COM 具有二進制兼容性。我們采用與 COM 一樣的跳轉表和調(diào)用約定。這意味著,一旦具有對 COM 的跨平臺支持,JNI 就能成為 Java 虛擬機的 COM 接口。

   我們認為 JNI 不應該是給定 Java 虛擬機所支持的唯一的本地方法接口。標準接口的好處在于程序員可以將自己的平臺相關代碼庫加載到不同的 Java 虛擬機上。在某些情況下,程序員可能不得不使用低層且與虛擬機有關的接口來獲得較高的效率。但在其它情況下,程序員可能使用高層接口來建立軟件組件。實際上,我們希望隨著 Java 環(huán)境和組件軟件技術發(fā)展得越來越成熟,本地方法將變得越來越不重要。

   --------------------------------------------------------------------------------
   利用 JNI 編程
   本地方法程序設計人員應開始利用 JNI 進行編程。利用 JNI 編程隔離了一些未知條件,例如終端用戶可能正在運行的廠商的虛擬機。遵守 JNI 標準是本地庫能在給定 Java 虛擬機上運行的最好保證。例如,雖然 JDK 1.1 將繼續(xù)支持 JDK 1.0 中所實現(xiàn)的舊式的本地方法接口,但是可以肯定的是 JDK 的未來版本將停止支持舊式的本地方法接口。依賴于舊式接口的本地方法將不得不重新編寫。

   如果您正在實現(xiàn) Java 虛擬機,則應該實現(xiàn) JNI。我們(Javasoft 和獲得許可方)盡力確保 JNI 不會占用虛擬機實現(xiàn)的系統(tǒng)開銷或施加任何限制,包括對象表示,垃圾收集機制等。如果您遇到了我們可能忽視了的問題,請告知我們。

   --------------------------------------------------------------------------------
   JDK 1.1.2 中的變化
   為了更好地支持 Java 運行時環(huán)境 (JRE),在 JDK 1.1.2 中對調(diào)用 API 在幾個方面作了擴展。這些變化沒有破壞任何已有代碼,JNI 本地方法接口也沒有改變。

   JDK1_1InitArgs 結構中的 reserved0 域已被重新命名為 version。JDK1_1InitArgs 結構保存 JNI_CreateJavaVM 的初始化參數(shù)。JNI_GetDefaultJavaVMInitArgs 和 JNI_CreateJavaVM 的調(diào)用者必須將版本域設置為 0x00010001。JNI_GetDefaultJavaVMInitArgs 被更改為返回 jint,用于表示是否支持所請求的版本。

   JDK1_1InitArgs 結構中的 reserved1 域已被重新命名為 properties。這是一個 NULL-終結的字符串數(shù)組。每個字符串具有以下格式:

   name=value
   表示系統(tǒng)屬性(該功能對應于 Java 命令行中的 -D 選項)。

   在 JDK 1.1.1 中,調(diào)用 DestroyJavaVM 的線程必須是虛擬機中的唯一用戶線程。JDK 1.1.2 放松了這一限制。如果調(diào)用 DestroyJavaVM 時有多個用戶線程,則虛擬機將等待直到當前線程成為唯一的用戶線程,然后銷毀自己。