Java Native Interface
Java Native Interface (JNI) は、Javaプラットフォームにおいて、Javaで記述されたプログラムと、他の言語(たとえばCやC++など)で書かれた、実際のCPUの上で動作するコード(ネイティブコード)とを連携するためのインタフェース仕様である。Java言語からネイティブコードを利用するためのABIと、逆にネイティブコードからJavaバイトコードを動作させるためのバーチャルマシンを利用するためのAPIの2つから成る。
JNIを使うことで、Java言語のバーチャルマシンで動作させるには処理速度の面で不利とされる計算量の多いプログラムを部分的にネイティブコードに置き換えて高速化したり、標準クラスライブラリからはアクセスできないオペレーティングシステムの機能を利用するプログラムを、あたかも通常のJavaクラスのように呼び出したりできるようになる。Java言語以外のJava VM上で動作する言語からも利用可能である。
JNIによる、Javaバーチャルマシンからネイティブコードの呼び出しは、バーチャルマシンの実行環境の一貫性を保つために、通常のJavaプログラムの実行時とは異なる例外的なメモリ管理や排他制御を必要とする場合があり、しばしばプログラムの実行速度の低下を招くことがある。そのため、単純にJNIを利用することでアプリケーション性能を改善できるというわけではない。
JNIの動作
JNIフレームワークでは、ネイティブ関数は.cもしくは.cppファイルに分離して実装する。C++はJNIに対して多少洗練されたインターフェイスを提供する。Java VMはJNIEnv
ポインタ、jobject
ポインタ、そしてJavaメソッドで定義されたすべてのJava引数を通して、ネイティブな関数を起動する。JNI関数は以下のような形式となる。
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj)
{
/*ネイティブコードをここに記述する*/
}
env
ポインタはJava VMへのインタフェースを含む構造体である。これはJava VMとの相互作用および、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とネイティブ間の変数型のマッピングを示す。
C/C++変数型 | Java変数型 | 説明 | 型シグニチャ |
---|---|---|---|
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 int32_t |
jint | signed 32 bits | I |
long long int64_t |
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
を使用した場合、そのコードはJava VMをクラッシュさせるだろう。
// !!! 誤ったコード !!! //
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)
{
//Java文字列よりネイティブ文字列を取得する
const char *nativeString = env->GetStringUTFChars(javaString, 0);
printf("%s", nativeString);
//この行を忘れてはいけない!
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);
関連項目
- Foreign function interface
- Java Native Access: JNI を用いずにネイティブコードを呼び出すためのライブラリ
- P/Invoke: .NET Frameworkにおける JNI と等価の仕組み
- SWIG: 多言語に対応したインターフェイス生成ツールで、C/C++のライブラリ用の JNI コードを生成する
外部リンク
- Java SE 7 Java Native Interface 関連 API および 開発者ガイド -- Oracle
- Java SE 7 Java Native Interface-related API's and Developer Guides -- Oracle
- Best practices for using the Java Native Interface
- GNU CNI Tutorial
- A JNI Tutorial at CodeProject.com (Microsoft specific)
- JNI Tutorial at CodeToad.com
- JNI in XCode from Apple