第一章:JNI介紹

這個章節介紹Java Native Interface(JNI)。JNI是一套編程接口。這套接口可是使運行在Java虛擬機中的Java代碼1和用其他語言編寫的庫文件(libraries)或者應用程序進行互操作(操作翻譯爲通信我覺得更恰當一點)。

JNI最重要的一點是,它對Java VM的JNI接口的實現沒有強加任何限制2。因此呢,Java VM提供者可以在不影響他原來VM設計的情況下來增加對JNI的支持。

本章包括以下幾個小節:

  • Java Native Interface 概述
  • JNI歷史背景
    • JDK1.0 的本機接口方法
    • Java運行時接口
    • 原始本機接口和Java / COM接口
  • 目標

Java Native Interface 概述

雖然您可以用Java來編寫程序,當時在有些情況下Java並不能滿足程序的需要。程序員需要通過JNI來編寫native方法3來處理這些情況。

以下示例將說明何時需要使用Java native方法:
* 標準的Java類庫不支持應用所需的平臺相關的功能的時候。
* 您已經有一個通過其他語言寫好的類庫,希望將它直接用到目前的Java項目中去的時候。
* 用Java代碼來處理很耗時間而時間有很關鍵的時候(就是代碼要求執行效率很高的時候,比如Android中處理bitmap時)。

在使用JNI編程過程中,JNI接口提供以下功能:

  • 創建、檢查、操作Java對象(包括arrays和strings)。
  • 調用Java方法。
  • 捕獲和跑出Java異常。
  • 加載Java類或者獲取類信息。
  • 執行運行時類型檢查。

你也可以用JNI的Invocation API4將Java VM嵌入到你的其他語言的程序用。這樣將使程序員可以輕鬆的在現有的程序中使用Java的功能。

JNI歷史背景

早期的時候,VMs的供應者們提供不同的本機方法接口來實現Java和其他語言的互通。這樣將使開發者不得不在對應的Java VM平臺來維護分發多個版本的JNI庫實現。

我們簡單的來看幾個本機方法實現方案的例子:

  • JDK1.0本機方法接口
  • Netscape5 的Java Runtime Interface。
  • 微軟的 Raw Native Interface and Java/COM interface。

JDK1.0 Native Method Interface

在JDK1.0中內置了一套本機方法接口。不幸的是,有兩個原因使它不適合於其他的Java虛擬機。

首先,平臺相關代碼將 Java 對象中的域作爲 C 結構的成員來進行訪問。然而,Java語言規範並沒有限制Java對象是如何在內存中佈置的。如果Java VM在內存中佈置對象的方式不同,程序開發者將不得不每次重新編譯本機方法庫。

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

Netscape’s Java Runtime Interface
Netscape 提供了 Java 運行時接口 (JRI),一種 Java 虛擬機的通用接口。JRI 的設計融入了可移植性—它幾乎沒有對底層 Java 虛擬機的實現細節作任何假設。而且JRI 廣泛討論了各種各樣的問題,包括本機方法、調試、反射、嵌入(調用)等等。

Raw Native Interface and Java/COM Interface
微軟實現了一個Java虛擬機,其 Java 虛擬機實現了兩種本機方法接口。在低一級,它提供了高效的原始本機接口 (RNI)。RNI 提供了與 JDK 本機方法接口有高度源代碼級的向後兼容性,儘管它們之間還有一個主要區別,即平臺相關代碼必須用 RNI 函數來與垃圾收集器進行顯式的交互,而不是依賴於保守的垃圾收集。
在高一級,微軟的 Java/COM 接口爲 Java 虛擬機提供了與語言無關的標準二進制接口。Java 代碼可以象使用 Java 對象一樣來使用 COM 對象。Java 類也可以作爲 COM 類顯示給系統的其餘部分。

JNI的目標

我們相信,一個統一而且標準的接口將爲每個人帶來以下好處:、

  • 虛擬機提供者可以支持更多的本機代碼(native code)。
  • 構建工具也不用再維護不同種類的本機方法接口(native method interfaces)。
  • 開發者只需要寫一個版本的本機代碼就可以在不同的VM上正確運行。

標準的本機方法接口的定義既然要兼容於不同的Java VM,就需要所有Java VM開發商或者個人或者對Java VM剛興趣的都參與進來。因此,我們同所有的Java相關人員組織了一系列的討論,來商議本機方法接口。討論得到以下結論,要實現標準的本機方法接口,必須滿足以下要求:

  • 二進制兼容性(Binary compatibility) - 二進制兼容性意思是說,在任何一個給定平臺上的所有Java虛擬機必須兼容本地方法庫(native method libraries)。7
  • 效率 - 爲了支持時間敏感( time-critical)的代碼,本地方法接口不能開銷太大。綜合目前所有已知技術來看,VM相對於JNI的獨立性(和二進制兼容性)是本機方法產生開銷的根本原因。所以某些情況下我們需要在VM的獨立性和本地方法效率之間達成妥協。
  • 功能 - 這套接口必須將足夠多的Java VM內部功能暴露出來,用以允許本機方法來執行所有需要的功能。

Java本地接口的最終實現(Approach)

我們希望採用一種現有的實現來作爲我們的標準接口,因爲這樣將減輕開發者的負擔。遺憾的是已有的解決方案中,沒有一種完全滿足我們的目標。

Netscape的JRI是最接近我們的目標的,因爲我們將JRI作爲參考進行設計。熟悉JRI的讀者或許會注意到很多的相似之處。可惜儘管我們盡了最大的努力,但是JNI與JRI依然是不兼容的,但這並不妨礙VM同時提供對JNI和JRI的支持。

微軟的RNI是對JDK 1.0中本地方法接口做了一些改進,解決了本機方法採用保守的垃圾回收的問題。但是RNI不適合作爲VM堆裏的本機方法接口。像JDK一樣,RNI本地方法將Java Object當做C的結構體來直接訪問,這將造成兩個問題:

  • RNI將Java Object在VM中的內存佈局暴露給了本地代碼(每家VM的實現有可能採用不同的內存佈局)。
  • 將 Java 對象作爲 C 結構直接進行訪問使得不可能有效地加入“寫屏障”,寫屏障是高級的垃圾收集算法所必需的(我也不明白垃圾回收和寫屏障的關係)。

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

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

  • 第一,Java/COM 接口缺少某些必需功能,例如訪問私有域和拋出普通異常。
  • 第二,Java/COM 接口自動爲 Java 對象提供標準的 IUnknown 和 IDispatch COM 接口,因而平臺相關代碼能夠訪問公有方法和域。遺憾的是,IDispatch 接口不能處理重載的 Java 方法,而且在匹配方法名稱時不區別大小寫。另外,通過 IDispatch 接口暴露的所有 Java 方法被打包在一起來執行動態類型檢查和強制轉換。這是因爲 IDispatch 接口的設計只考慮到了弱類型的語言(例如 Basic)。
  • 第三,COM 允許軟件組件(包括完全成熟的應用程序)一起工作,而不是處理單個低層函數。我們認爲將所有 Java 類或低層本地方法都當作軟件組件是不恰當的。
  • 第四,在 UNIX 平臺上由於缺少對 COM 的支持,所以阻礙了直接採用 COM。

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

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

使用JNI進行編程

本地方法的編寫者應該利用JNI進行編程,這樣將隔離開一些未知情況,比如中斷正在使用的哪一個廠商的VM。通過使用JNI,你的本機代碼庫在任何給定的Java VM中將會有一個更好的運行環境。

如果你是Java VM提供者,你應該支持JNI。經過時間的驗證,JNI不會對您的VM實現造成任何的開銷或者限制,包括對象的標示,垃圾回收方案等。如果遇到任何我們沒有注意到的問題,您可以隨時反饋給我們。

改變

從Java SE6.0開始,移除了已經棄用的結構體JDK1_1InitArgs和JDK1_1AttachArgs,使用JavaVMInitArgs和JavaVMAttachArgs代替。

[1] 其他語言:一般來說只有C、C++、彙編。事實上只要能提供通過C代碼來調用的接口就可以。
[2] 這句話看起來或許會使大家感到迷茫,它的意思是說,JNI只是Sun公司定義的一套接口規範,對各家Java VM的實現沒有限制。
[3] 在.java文件中定義方法前面加native關鍵字,以及其所對應的C方法,即爲native方法。
[4] JNI中的一套接口,第五章會有翻譯。
[5] 網景公司,曾經很厲害的一家公司。
[6] JDK 1.0 的本機方法接口依賴於保守的垃圾收集器。例如,無限制地使用 unhand 宏使得有必要以保守方式掃描本機堆棧。
[7] 這句話或許有點難以理解,翻譯的也不是很好,簡單點說,就是在一個平臺可以使用的已經編譯好(編譯爲二進制文件)的本機方法在其他平臺必須也可以使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章