目次 | 前へ |
この呼び出し API により、ソフトウェアベンダーは Java VM を任意のネイティブアプリケーションにロードできるようになります。そのベンダーの提供する Java が実行可能なアプリケーションは、Java VM ソースコードにリンクする必要がありません。
この章では、呼び出し API の概要の説明から始めます。それ以降は、すべての呼び出し API 関数のリファレンスページです。
次のコード例では、呼び出し API の関数の使用方法について説明します。この例では、C++ コードは Java VM を生成し、Main.test
と呼ばれる static メソッドを呼び出します。明確にするために、エラーチェックを省略しました。
#include <jni.h> /* where everything is defined */ ... JavaVM *jvm; /* denotes a Java VM */ JNIEnv *env; /* pointer to native method interface */ JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */ JavaVMOption* options = new JavaVMOption[1]; options[0].optionString = "-Djava.class.path=/usr/lib/java"; vm_args.version = JNI_VERSION_1_6; vm_args.nOptions = 1; vm_args.options = options; vm_args.ignoreUnrecognized = false; /* load and initialize a Java VM, return a JNI interface * pointer in env */ JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); delete options; /* invoke the Main.test method using the JNI */ jclass cls = env->FindClass("Main"); jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V"); env->CallStaticVoidMethod(cls, mid, 100); /* We are done. */ jvm->DestroyJavaVM();
この例では、API の 3 つの関数を使用しています。呼び出し API は、ネイティブアプリケーションが JNI インタフェースポインタを使用して VM 機能にアクセスできるようにします。この設計は Netscape の JRI 埋め込みインタフェースに似ています。
JNI_CreateJavaVM()
関数は Java VM をロードして初期化し、JNI インタフェースポインタへのポインタを返します。JNI_CreateJavaVM()
を呼び出したスレッドは、メインスレッドとみなされます。
JNI インタフェースポインタ (JNIEnv
) は、現在のスレッドでのみ有効です。別のスレッドが Java VM にアクセスする必要がある場合、これは最初に AttachCurrentThread()
を呼び出して、自身を VM に接続し JNI インタフェースポインタを取得する必要があります。一度 VM に接続されると、ネイティブスレッドはネイティブメソッド内で実行中の普通の Java スレッドのように機能します。ネイティブスレッドは、DetachCurrentThread()
を呼び出して自身を切り離すまで VM に接続されたままになります。
接続されたスレッドには、相当量の作業を実行するのに十分なスタック領域が必要です。スレッドごとのスタック領域の割り当ては、オペレーティングシステム固有です。たとえば、pthread を使用する場合は、pthread_create
への pthread_attr_t
引数でスタックサイズを指定できます。
VM に接続されたネイティブスレッドは、終了する前に DetachCurrentThread()
を呼び出して自身を分離する必要があります。呼び出しスタック上に Java メソッドがある場合、スレッドは分離できません。
JNI_DestroyJavaVM()
関数は、Java VM をアンロードします。JDK/JRE 1.1 では、DestroyJavaVM
を呼び出して VM をアンロードできるのはメインスレッドだけでした。JDK/JRE 1.2 では、この制限が削除され、どのスレッドでも DestroyJavaVM
を呼び出して VM をアンロードできます。
VM は、現在のスレッドを実際にアンロードする前に、それがデーモンでない唯一のユーザースレッドになるまで待機します。ユーザースレッドには、Java スレッドおよび接続されたネイティブスレッドの両方があります。この制限は、Java スレッドまたは接続されたネイティブスレッドがロック、ウィンドウなどのシステムリソースを保持している可能性があるために存在します。VM
は、自動的にこれらのリソースを解放することはできません。VM がアンロードされているときに、現在のスレッドを実行中の唯一のスレッドに制限することにより、任意のスレッドが保持しているシステムリソースを解放する負荷はプログラマに課せられます。
JDK/JRE 1.2 では、クラスローダーが各自のネイティブライブラリのセットを管理します。同じ JNI ネイティブライブラリを、2 つ以上のクラスローダーにロードすることはできません。そのようなことを行うと、UnsatisfiedLinkError
がスローされます。たとえば、System.loadLibrary
を使用して 2 つのクラスローダーにネイティブライブラリをロードしようとすると、UnsatisfiedLinkError
がスローされます。この新しい手法の利点は次のとおりです。
バージョン管理およびリソース管理を容易にするために、JDK/JRE 1.2 の JNI ライブラリは次の 2 つの関数をオプションでエクスポートできます。
jint JNI_OnLoad(JavaVM *vm, void *reserved);
System.loadLibrary
を介して) ネイティブライブラリがロードされると、VM は JNI_OnLoad
を呼び出します。JNI_OnLoad
は、ネイティブライブラリが必要とする JNI バージョンを返さなければなりません。
新しい JNI 関数のどれかを使用するために、ネイティブライブラリは JNI_VERSION_1_2
を返す JNI_OnLoad
関数をエクスポートする必要があります。ネイティブライブラリが JNI_OnLoad
関数をエクスポートしない場合、VM はライブラリが JNI バージョン JNI_VERSION_1_1
を要求しているだけであるとみなします。VM が JNI_OnLoad
によって返されたバージョン番号を認識しない場合、ネイティブライブラリをロードすることはできません。
ネイティブメソッド実装を含むネイティブライブラリからエクスポートされます。
JDK/JRE 1.4
JDK/JRE 1.1 で使用可能であった JNI 関数に加え、J2SE リリース 1.2 で導入された JNI 関数を使用するには、ネイティブライブラリは JNI_VERSION_1_2
を返す JNI_OnLoad
関数をエクスポートする必要があります。
リリース 1.2 で利用できた JNI 関数に加え、J2SE リリース 1.4 に導入された JNI 関数を使用する場合、ネイティブライブラリは JNI_VERSION_1_4
を返す JNI_OnLoad
関数をエクスポートする必要があります。
ネイティブライブラリが JNI_OnLoad
関数をエクスポートしない場合、VM はライブラリが JNI バージョン JNI_VERSION_1_1
を要求しているだけであるとみなします。VM が JNI_OnLoad
によって返されたバージョン番号を認識しない場合、ネイティブライブラリをロードすることはできません。
void JNI_OnUnload(JavaVM *vm, void *reserved);
JNI_OnUnload
を呼び出します。この関数は、クリーンアップオペレーションに使用されます。これは未確認のコンテキスト (ファイナライザからのコンテキストなど) で呼び出される関数なので、プログラマは慎重に Java VM サービスを使用する必要があります。また Java コールバックを任意に行うことのないようにしなければなりません。
JNI_OnLoad
と JNI_OnUnload
は、JNI ライブラリがオプションで提供する 2 つの関数であり、VM からエクスポートされるものではありません。
JavaVM
型は、呼び出し API 関数テーブルへのポインタです。次のコード例は、この関数テーブルを示しています。
typedef const struct JNIInvokeInterface *JavaVM; const struct JNIInvokeInterface ... = { NULL, NULL, NULL, DestroyJavaVM, AttachCurrentThread, DetachCurrentThread, GetEnv, AttachCurrentThreadAsDaemon };
3 つの呼び出し API 関数 JNI_GetDefaultJavaVMInitArgs()
、JNI_GetCreatedJavaVMs()
、および JNI_CreateJavaVM()
は、Java VM 関数ではありません。これらの関数は既存の JavaVM
構造体がなくても使用できます。
jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);
Java VM のデフォルト構成を返します。この関数を呼び出す前に、ネイティブコードは vm_args->version フィールドを、VM でサポートされると予測される JNI バージョンに設定する必要があります。この関数から復帰すると、vm_args->version は、VM がサポートする実際の JNI バージョンに設定されます。
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
vm_args
: デフォルト引数が入る JavaVMInitArgs
構造体を指すポインタ。
要求されたバージョンがサポートされている場合は JNI_OK
を返し、要求されたバージョンがサポートされていない場合は JNI エラーコード (負の数) を返します。
jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);
作成された Java VM をすべて返します。VM へのポインタは、作成された順にバッファー vmBuf に書き込まれます。しかし、エントリの bufLen 番号しか書き込みません。作成された VM の全体数は、*nVM で返します。
JDK/JRE 1.2 では、1 つのプロセスでの複数の VM の作成はサポートされていません。
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
vmBuf
: VM 構造体が配置されるバッファーへのポインタ。
bufLen
: バッファーの長さ。
nVMs
: 整数を指すポインタ。
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);
ロードして、Java VM を初期化します。現在のスレッドがメインスレッドになります。env
引数を、メインスレッドの JNI インタフェースポインタに設定します。
JNI_CreateJavaVM
の 2 番目の引数が常に JNIEnv *
を参照するポインタであるのに対して、3 番目の引数は、オプション文字列を使用してすべての VM 起動オプションをエンコードする JavaVMInitArgs
構造体を参照するポインタです。
typedef struct JavaVMInitArgs { jint version; jint nOptions; JavaVMOption *options; jboolean ignoreUnrecognized; } JavaVMInitArgs;
version
フィールドは、少なくとも JNI_VERSION_1_2
に設定する必要があります。options
フィールドは、次の型の配列です。
typedef struct JavaVMOption { char *optionString; /* the option as a string in the default platform encoding */ void *extraInfo; } JavaVMOption;
配列のサイズは、JavaVMInitArgs
の nOptions フィールドに示されます。ignoreUnrecognized
が JNI_TRUE
の場合、JNI_CreateJavaVM
は、「-X
」または「_
」で始まるすべての認識できないオプション文字列を無視します。ignoreUnrecognized
が JNI_FALSE
の場合、JNI_CreateJavaVM
は認識できないオプション文字列を検出すると、ただちに JNI_ERR
を返します。すべての Java 仮想マシンは、次の標準オプションのセットを認識する必要があります。
optionString | 意味 |
---|---|
-D<name>=<value> |
システムプロパティーを設定する |
-verbose[:class|gc|jni] |
冗長出力を有効にする。各オプションの後に、VM が出力するメッセージの種類を示す、コンマで区切った名前のリストを続けることができる。たとえば、「-verbose:gc,class 」は、VM に GC とクラスローディング関連のメッセージを出力するように指示する。標準的な名前には、gc 、class 、および jni 。標準でない (VM 固有の) 名前はすべて、「X 」で始まる必要がある。 |
vfprintf |
extraInfo は、vfprintf フックへのポインタ。 |
exit |
extraInfo は、exit フックへのポインタ。 |
abort |
extraInfo は、abort フックへのポインタ。 |
加えて、各 VM 実装は、標準でない独自のオプション文字列のセットをサポートします。標準でないオプション名は、「-X
」または下線 (「_
」) で始まる必要があります。たとえば、JDK/JRE は -Xms
および -Xmx
オプションをサポートしているため、プログラマは初期および最大のヒープサイズを指定できます。「-X
」で始まるオプションは、「java
」コマンド行からアクセス可能です。
次の例は、JDK/JRE で Java 仮想マシンを作成するコードです。
JavaVMInitArgs vm_args; JavaVMOption options[4]; options[0].optionString = "-Djava.compiler=NONE"; /* disable JIT */ options[1].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */ options[2].optionString = "-Djava.library.path=c:\mylibs"; /* set native library path */ options[3].optionString = "-verbose:jni"; /* print JNI-related messages */ vm_args.version = JNI_VERSION_1_2; vm_args.options = options; vm_args.nOptions = 4; vm_args.ignoreUnrecognized = TRUE; /* Note that in the JDK/JRE, there is no longer any need to call * JNI_GetDefaultJavaVMInitArgs. */ res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args); if (res < 0) ...
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
p_vm
: 結果の VM 構造体が配置される位置へのポインタ。
p_env
: メインスレッドの JNI インタフェースポインタが配置される位置へのポインタ。
vm_args
: Java VM 初期化引数。
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
jint DestroyJavaVM(JavaVM *vm);
Java VM をアンロードし、そのリソースを回復します。
JDK/JRE 1.1 では、DestroyJavaVM
は完全にサポートされているわけではありません。JDK/JRE 1.1 では、メインスレッドだけが DestroyJavaVM
を呼び出すことができます。 JDK/JRE 1.2 以降、どのスレッドでも、接続されているかどうかにかかわらず、この関数を呼び出せます。現在のスレッドが接続されている場合、VM は、現在のスレッドがデーモンでない唯一のユーザーレベル Java スレッドになるまで待機します。現在のスレッドが接続されていない場合は、VM が現在のスレッドを接続し、現在のスレッドがデーモンでない唯一のユーザーレベルのスレッドになるまで待機します。ただし、VM のアンロードは、JDK/JRE でもサポートされません。
JavaVM インタフェース関数テーブルのインデックス 3。
vm
: 破棄される Java VM。
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
JDK/JRE 1.1.2 では、VM のアンロードはサポートされていません。
jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);
現在のスレッドを Java VM へ接続します。JNIEnv
引数で JNI インタフェースポインタを返します。
すでに接続されているスレッドへの接続は、無操作です。
ネイティブスレッドを 2 つの Java VM へ同時に接続することはできません。
スレッドが VM に接続されている場合、コンテキストクラスのローダーは、ブートストラップローダーです。
JavaVM インタフェース関数テーブルのインデックス 4。
vm
: 現在のスレッドが接続される VM。
p_env
: 現在のスレッドの JNI インタフェースポインタが配置される位置へのポインタ。
thr_args
: NULL または JavaVMAttachArgs
構造体を参照するポインタにして、追加情報を指定できます。
JDK/JRE 1.1 では、AttachCurrentThread
の 2 番目の引数は常に JNIEnv
を参照するポインタです。AttachCurrentThread
への 3 番目の引数は予約されており、NULL
に設定しなければなりません。
JDK/JRE 1.2 では、1.1 の動作をさせるには 3 番目の引数として NULL
を渡します。または、次の構造体にポインタを渡して追加情報を指定できます。
typedef struct JavaVMAttachArgs { jint version; /* must be at least JNI_VERSION_1_2 */ char *name; /* the name of the thread as a modified UTF-8 string, or NULL */ jobject group; /* global ref of a ThreadGroup object, or NULL */ } JavaVMAttachArgs
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);
AttachCurrentThread とセマンティクスは同じですが、新しく作成された java.lang.Thread インスタンスはデーモンです。
スレッドがすでに AttachCurrentThread または AttachCurrentThreadAsDaemon を介して接続されている場合、このルーチンは、penv が指している値を現在のスレッドの JNIEnv に設定します。この場合、AttachCurrentThread もこのルーチンも、スレッドのデーモンステータスに影響しません。
JavaVM インタフェース関数テーブルのインデックス 7。
vm: 現在のスレッドが接続される仮想マシンインスタンス。
penv: 現在のスレッドの JNIEnv インタフェースポインタが配置される位置へのポインタ。
args: JavaVMAttachArgs 構造体へのポインタ。
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
なし。
JDK/JRE 1.4
jint DetachCurrentThread(JavaVM *vm);
Java VM から現在のスレッドを分離します。このスレッドが保持する Java モニターはすべて解放されます。このスレッドが終了するのを待つ Java スレッドすべてに、通知が行われます。
JDK/JRE 1.2 では、VM からメインスレッドを切り離すことができます。JavaVM インタフェース関数テーブルのインデックス 5。
vm
: 現在のスレッドが分離される VM。
成功した場合は JNI_OK
を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。
vm
: インタフェース取得元の仮想マシンインスタンス。env
: 現在のスレッドの JNI インタフェースポインタが配置される位置へのポインタ。version
: 要求された JNI バージョン。現在のスレッドが VM に接続されていない場合は、*env
を NULL
に設定し、JNI_EDETACHED
を返します。指定されたバージョンがサポートされていない場合は、*env
を NULL
に設定し、JNI_EVERSION
を返します。それ以外の場合は、*env
を適切なインタフェースに設定し、JNI_OK
を返します。
JDK/JRE 1.2
目次 | 前へ |