Java本地介面JNI詳解

來源:互聯網
上載者:User

標籤:

對於java程式員來說,java語言的好處和優點,我想不用我說了,大家自然會說出很多一套套的。但雖然我們作為java程式員,但我們不得不承認java語言也有一些它本身的缺點。比如在效能、和底層打交道方面都有它的缺點。所以java就提供了一些本地介面,他主要的作用就是提供一個標準的方式讓java程式通過虛擬機器與原生代碼進行互動,這也就是我們平常常說的java本地介面(JNI——java native Interface)。它使得在 Java 虛擬機器(VM) 內部啟動並執行Java 代碼能夠與用其它程式設計語言(如 C、C++ 和組合語言)編寫的應用程式和庫進行互操作。JNI 最重要的好處是它沒有對底層 Java 虛擬機器的實現施加任何限制。因此,Java虛擬機器廠商可以在不影響虛擬機器其它部分的情況下添加對JNI 的支援。程式員只需編寫一種版本的本地應用程式或庫,就能夠與所有支援JNI 的Java 虛擬機器協同工作。我們來看一下為什麼要與原生代碼進行互動:


一:提高應用程式效能。我們知道java對於c/c++、組合語言來說,顯得比較“進階”。其實這裡的進階就是簡化了程式員的工作。很多底層的東西都讓java虛擬機器做了。但畢竟相對於直接存取底層來講,java多了一步虛擬機器的過程,所以在效能上比著這些原生語言稍微有點慢。


二:實現一些與底層相關的功能。Java平台提供的標準類庫,還有強大的API,雖然能完成大部分功能。但有些和底層硬體打交道的功能在java API提供的類庫中還是無法完成。


三:與已有的使用原生代碼編寫的程式進行整合。在於作業系統上由c或者c++等原生語言編寫的軟體進行集成的時候,可以用JNI。


JNI 介面函數和指標

平台相關代碼是通過調用 JNI 函數來訪問Java 虛擬機器功能的。JNI 函數可通過介面指標來獲得。介面指標是指標的指標,它指向一個指標數組,而指標數組中的每個元素又指向一個介面函數。每個介面函數都處在數組的某個預定位移量中。說明了介面指標的組織圖。

JNI 介面的組織類似於C++ 虛擬函數表或COM 介面。使用介面表而不使用硬性編入的函數表的好處是使JNI 名字空間與平台相關代碼分開。虛擬機器可以很容易地提供多個版本的JNI 函數表。例如,虛擬機器可支援以下兩個JNI 函數表:

1)一個表對非法參數進行全面檢查,適用於偵錯工具;

2)另一個表只進行 JNI 規範所要求的最小程度的檢查,因此效率較高。

JNI 介面指標只在當前線程中有效。因此,本地方法不能將介面指標從一個線程傳遞到另一個線程中。實現 JNI 的虛擬機器可將本地線程的資料分配和儲存在 JNI 介面指標所指向的地區中。


本地方法將JNI 介面指針當作參數來接受。虛擬機器在從相同的 Java 線程中對本地方法進行多次調用時,保證傳遞給該本地方法的介面指標是相同的。但是,一個本地方法可被不同的 Java 線程所調用,因此可以接受不同的 JNI 介面指標。

1)編寫Java類代碼

其中,需要JNI實現的方法應當用native關鍵字聲明,在該類中,用System.loadLibrary()方法載入需要的動態連結程式庫,關鍵代碼如下:

//Compute.javapublic class Compute{    public native double sqrt(double  params);    static{        //調用動態連結程式庫        System.loadLibrary("compute");    }}

    
2)編譯成位元組代碼

在這個過程中,由於採用了native關鍵字聲明,Java編譯器會忽視沒有代碼體的JNI方法部分。
   
3)產生相關JNI方法的標頭檔

這個過程的實現一般是通過利用jlavah-jni  * class產生的(-jni可以省略),也可以手工產生該檔案;但是由於 Java 虛擬機器是根據一定的命名規範完成對JNI方法的調用,所以手工編寫標頭檔需要特別小心。
上述檔案產生的標頭檔部分代碼如下:

//Compute.hextern“C”{JNIEXPORT jdoubleJNICALL Java_Compute_comp(JNI-Env *, jobject, jdoubleArray);

JNI函數名稱分為三部分:首先是Java關鍵字,供Java虛擬機器識別;然後是調用者類名稱(全限定的類名,其中用底線代替名稱分隔字元);最後是對應的方法名稱,各段名稱之間用底線分割。


JNI函數的參數也由三部分組成:首先是JNIEnv *,是一個指向JNI運行環境的指標;第二個參數隨本地方法是靜態還是非靜態而有所不同一一非靜態本地方法的第二個參數是對對象的引用,而靜態本地方法的第二個參數是對其Java類的引用;其餘的參數對應通常Java方法的參數,參數類型需要根據一定規則進行映射。


4)編寫相應方法的實現代碼

在編碼過程中,需要注意變數的長度問題,例如Java的整型變數長度為32位,而C語言為16位,所以要仔細核對變數類型映射表,防止在傳值過程中出現問題。


 5)將JNI實現代碼編譯成動態連結程式庫

編譯過程是利用C/C++編譯器實現的,在windows平台上,編譯和串連的結果是動態連結程式庫DLL檔案。當要使用產生的動態連結程式庫時,調用者類中需要顯式調用該連結庫dll檔案。
經過上述處理,基本上完成了一個包含本地化方法的Java類的開發。


附錄:將Jav類型映射到本地C 類型 

為了使用方便,特提供以下定義。

#define JNI_FALSE  0

#define JNI_TRUE   1

jsize 整數類型用於描述主要指數和大小:

typedef jint jsize;


故障排除

當使用 JNI 從Java 程式訪問機器碼時,您會遇到許多問題。您會遇到的三個最常見的錯誤是:

1)無法找到動態連結。它所產生的錯誤訊息是:java.lang.UnsatisfiedLinkError。這通常指無法找到共用庫,或者無法找到共用庫內特定的本機方法。

2)無法找到共用庫檔案。當用 System.loadLibrary(String libname) 方法(參數是檔案名稱)裝入庫檔案時,請確保檔案名稱拼字正確以及沒有指定副檔名。還有,確保庫檔案的位置在類路徑中,從而確保 JVM 可以訪問該庫檔案。

3)無法找到具有指定說明的方法。確保您的 C/C++ 函數實現擁有與標頭檔中的函數說明相同的說明。


結束語

從 Java 調用 C 或 C++ 機器碼(雖然不簡單)是 Java 平台中一種良好整合的功能。雖然 JNI 支援 C 和 C++,但 C++ 介面更清晰一些並且通常比 C 介面更可取。正如您已經看到的,調用 C 或 C++ 機器碼需要賦予函數特殊的名稱,並建立共用庫檔案。當利用現有程式碼程式庫時,更改代碼通常是不可取的。要避免這一點,在C++ 中,通常建立代理代碼或代理類,它們有專門的 JNI 所需的命名函數。然後,這些函數可以調用底層庫函數,這些庫函數的說明和實現保持不變。

Java本地介面JNI詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.