コンテンツにスキップ

Java Native Interface

出典: フリー百科事典『ウィキペディア(Wikipedia)』

これはこのページの過去の版です。Dinamik-bot (会話 | 投稿記録) による 2012年11月27日 (火) 10:11個人設定で未設定ならUTC)時点の版 (r2.6.2) (ロボットによる: カテゴリ変更 pt:JNIpt:Java Native Interface)であり、現在の版とは大きく異なる場合があります。

Java Native Interface (JNI) は、Javaプラットフォームにおいて、Javaで記述されたプログラムと、他の言語(たとえばCC++など)で書かれた、実際のCPUの上で動作するコード(ネイティブコード)とを連携するためのインタフェース仕様である。Java言語からネイティブコードを利用するためのABIと、逆にネイティブコードからJavaのバイトコードを動作させるためのバーチャルマシンを利用するためのAPIの2つから成る。

JNIを使うことで、Java言語のバーチャルマシンで動作させるには処理速度の面で不利とされる計算量の多いプログラムを部分的にネイティブコードに置き換えて高速化したり、標準クラスライブラリからはアクセスできないオペレーティングシステムの機能を利用するプログラムを、あたかも通常のJavaクラスのように呼び出したりできるようになる。

更に、最近流行のJRuby等Javaで記述されたスクリプト言語からも同じように呼び出せるので、非常に強力である。

JNIによる、Javaバーチャルマシンからネイティブコードの呼び出しは、バーチャルマシンの実行環境の一貫性を保つために、通常のJavaプログラムの実行時とは異なる例外的なメモリ管理や排他制御を必要とする場合があり、しばしばプログラムの実行速度の低下を招くことがある。そのため、単純にJNIを利用することでアプリケーション性能を改善できると言うことはできない。

このJNIはEclipseで用いられているSWTにも使われている。

JNIの動作

JNIフレームワークでは、ネイティブ関数は.cもしくは.cppファイルに分離して実装する。C++はJNIに対して多少洗練されたインターフェイスを提供する。JVMはJNIEnvポインタ、jobjectポインタ、そしてJavaメソッドで定義されたすべてのJava引数を通して、ネイティブな関数を起動する。JNI関数は以下のような形式となる。  

 JNIEXPORT void JNICALL Java_ClassName_MethodName
   (JNIEnv *env, jobject obj)
 {
     /*ネイティブコードをここに記述する*/
 }

  envポインタはJVMへのインタフェースを含む構造体である。これはJVMとの相互作用および、Javaオブジェクトとの連携に必要な全ての関数を含んでいる。JNI関数の例としては、ネイティブ配列とJava配列の相互変換、ネイティブ文字列とJava文字列の相互変換、オブジェクトのインスタンス化、例外の送出などが挙げられる。基本的には、Javaコードでできることは全てJNIEnvを用いて行うことができる。以下の例示ではJava文字列をネイティブな文字列に変換する。  

 //C++ code
 JNIEXPORT void JNICALL Java_ClassName_MethodName
   (JNIEnv *env, jobject obj, jstring javaString)
 {
     //Java文字列よりネイティブ文字列を取得する
     const char *nativeString = env->GetStringUTFChars(javaString, 0);

     //ネイティブ文字列に関する処理

     //この行を忘れてはいけない!
     env->ReleaseStringUTFChars(javaString, nativeString);
 }

 

 /*C code*/
 JNIEXPORT void JNICALL Java_ClassName_MethodName
   (JNIEnv *env, jobject obj, jstring javaString)
 {
     /*Java文字列よりネイティブ文字列を取得する*/
     const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);

     /*ネイティブ文字列に関する処理*/

     /*この行を忘れてはいけない!*/
     (*env)->ReleaseStringUTFChars(env, javaString, nativeString);
 }

  C++ではJavaのように、オブジェクトのメソッドという概念を持つため、C++のJNIコードはCのそれに比べて文法的に多少クリアである。 Cではenvパラメータは(*env)->で被参照され、env はオブジェクトメソッド起動セマンティクスの一部として、明示的にJNIEnvメソッドに渡されなければならない。 C++ではenvパラメータはenv->で被参照される。envパラメータはオブジェクトメソッド起動セマンティクスの一部として暗黙的に渡される。

ネイティブ変数型とJava変数型はそれぞれ相互変換をすることができる。オブジェクトや配列、文字列などの合成型のために、ネイティブコードはJNIEnvのメソッド呼び出しにおいて、明示的なデータの変換を行う必要がある。

変数型のマッピング

以下の表ではJavaとネイティブ間の変数型のマッピングを示す。

ネイティブ変数型 Java変数型 Description Type signature
unsigned char jboolean unsigned 8 bits Z
signed char jbyte signed 8 bits B
unsigned short jchar unsigned 16 bits C
short jshort signed 16 bits S
int jint signed 32 bits I

long long
__int64

jlong signed 64 bits J
float jfloat 32 bits F
double jdouble 64 bits D

"L fully-qualified-class"という記述はクラスが名前によって一意に識別できることを示す。 例えば"Ljava/lang/String"はjava.lang.Stringを参照する。また、冒頭の[はその型の配列を意味する。例えば、[Iはint型の配列である。

これらの型は交換可能である。開発者は型キャスト無しで、int型と同様にjintを使用することができる。jint型からint型への変換も同様である。

しかし、文字列や配列のJava-ネイティブ間のマッピングについては様相が異なる。もし、char *のあるべき場所にjstringを使用した場合、そのコードはJVMをクラッシュさせるだろう。  

 // !!! 誤ったコード !!! // 
 JNIEXPORT void JNICALL Java_ClassName_MethodName
   (JNIEnv *env, jobject obj, jstring javaString)
 {
     printf("%s", javaString);
 }

 

 // 正しいコード //
 JNIEXPORT void JNICALL Java_ClassName_MethodName
   (JNIEnv *env, jobject obj, jstring javaString)
 {
     //Get the native string from javaString
     const char *nativeString = env->GetStringUTFChars(javaString, 0);
     printf("%s", nativeString);
     //DON'T FORGET THIS LINE!!!
     env->ReleaseStringUTFChars(javaString, nativeString);
 }

 

このことはJava配列についても同様である。配列の全ての要素の合計を得る例を用いて以下に示す。

 // !!! 誤ったコード !!! //
 JNIEXPORT jint JNICALL 
     Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
 {
      int i, sum = 0;
      for (i = 0; i < 10; i++) {
          sum += arr[i];
      }
      return sum;
 }

 

 // 正しいコード //
 JNIEXPORT jint JNICALL 
     Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
 {
      jint buf[10];
      jint i, sum = 0;
      env->GetIntArrayRegion(arr, 0, 10, buf);
      for (i = 0; i < 10; i++) {
          sum += buf[i];
      }
      return sum;
 }

JNIEnv*

JNIEnv引数は呼び出しの間のみ有効となる。呼び出しの外で引数を使う為には、以下のようにAttachCurrentThreadとDetachCurrentThreadを使用する必要がある。

JNIEnv *env;
(*g_vm)->AttachCurrentThread (g_vm, (void **) &env, NULL);
// do stuff
(*g_vm)->DetachCurrentThread (g_vm);


関連項目

外部リンク