目次 | 前の項目 | Java Native Interface 仕様 |
この呼び出し API により、ソフトウェアベンダーは Java VM を任意のネイティブアプリケーションにロードできるようになります。そのベンダーの提供する Java が実行可能なアプリケーションは、Java VM ソースコードにリンクする必要がありません。
この章では、呼び出し API の概要の説明から始めます。それ以降は、すべての呼び出し API 関数のリファレンスページです。
Java VM の組み込み機能を向上させるため、JDK 1.1.2 の呼び出し 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 */ JDK1_1InitArgs vm_args; /* JDK 1.1 VM initialization arguments */ vm_args.version = 0x00010001; /* New in 1.1.2:VM version */ /* Get the default initialization arguments and set the class * path */ JNI_GetDefaultJavaVMInitArgs(&vm_args); vm_args.classpath = ...; /* load and initialize a Java VM, return a JNI interface * pointer in env */ JNI_CreateJavaVM(&jvm, &env, &vm_args); /* 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 から分離できません。代わりに、VM 全体をアンロードするために DestroyJavaVM()
を呼び出す必要があります。
VM
は、メインスレッドが唯一のユーザスレッドになるまで待機し、そのあとアンロードを実行します。ユーザスレッドには、Java
スレッドおよび接続されたネイティブスレッドの両方があります。この制限は、Java
スレッドまたは接続されたネイティブスレッドがロック、ウィンドウなどのシステムリソースを保持している可能性があるために存在します。VM
は、自動的にこれらのリソースを解放することはできません。VM
がアンロードされているときに、メインスレッドを唯一の実行中のスレッドであると制限することで、任意のスレッドが保持するシステムリソースを解放する負
荷はプログラマに課せられます。
JDK では、各クラスローダは、独自のネイティブライブラリのセットを管理します。同じ JNI
ネイティブライブラリを、2 つ以上のクラスローダにロードすることはできません。 ロードしようとすると、UnsatisfiedLinkError
が発生します。たとえば、System.loadLibrary
を使用して 2
つのクラスローダにネイティブライブラリをロードしようとすると、UnsatisfiedLinkError
が発生します。この新しい手法の利点は次のとおりです。
バージョン管理およびリソース管理を容易にするために、Java 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 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);
ネイティブライブラリを含むクラスローダのガベージコレクションの際に、VM は 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 バージョンに設定しておかなければなりません1。 JDK 1.1.2 では、vm_args->version は、0x00010001 に設定する必要があります。この関数から復帰すると、vm_args->version は、VM がサポートする実際の JNI バージョンに設定されます。
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
vm_args
: デフォルト引数が入る VM
固有の初期化構造体へのポインタ
要求されたバージョンがサポートされている場合は「0」、要求されたバージョンがサポートされていない場合は負の数を 返します。
jint JNI_GetCreatedJavaVMs(JavaVM
**vmBuf, jsize bufLen,
jsize *nVMs);
作成された Java VM をすべて返します。VM へのポインタは、作成された順にバッファ vmBuf に書き込まれます。しかし、エントリの bufLen 番号しか書き込みません。作成された VM の全体数は、*nVM で返します。
JDK 1.1.2 は 1 つのプロセスで、1 つの VM しかサポートしません。
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
vmBuf
: VM
構造体が配置されるバッファへのポインタ
bufLen
: バッファの長さ
nVMs
: 整数を参照するポインタ
成功の場合は「0」を、失敗の場合は負の値を返します。
jint JNI_CreateJavaVM(JavaVM
**p_vm, JNIEnv **p_env, void *vm_args);
ロードして、Java VM を初期化します。現在のスレッドがメインスレッドになります。env
引数を、メインスレッドの JNI インタフェースポインタへ設定します。
JDK 1.1 は 1 つのプロセスで、1 つの VM しかサポートしません。vm_args のバージョンフィールドは、0x00010001 に設定する必要があります2。
JDK 1.1 では、JNI_CreateJavaVM
への 2 番目の引数は常に JNIEnv
*
へのポインタでした。3 番目の引数は、JDK 1.1 に固有の構造体 (JDK1_1InitArgs
)
へのポインタです。 JDK1_1InitArgs
構造は、すべての VM
に移植性のあるものとして設計されていないことは明らかです。
JDK では、標準 VM 初期化構造が導入されます。下位互換性は保持されます。VM 初期化引数が JDK1_1InitArgs
構造を指す場合、JNI_CreateJavaVM
は JNI インタフェースポインタの 1.1
バージョンを返します。3 番目の引数が JavaVMInitArgs
構造体にポイントする場合、VM は、JNI
インタフェースポインタの 1.2 バージョンを返します。固定オプションセットを含む JDK1_1InitArgs
と異なり、JavaVMInitArgs
はオプション文字列を使用して、任意の VM 起動オプションを符号化します。
typedef struct JavaVMInitArgs {
jint version;
jint nOptions;
JavaVMOption *options;
jboolean ignoreUnrecognized;
} JavaVMInitArgs;
version
フィールドは JNI_VERSION_1_2
に設定する必要があります。(逆に、JDK1_1InitArgs
のバージョンフィールドは、JNI_VERSION_1_1
に設定する必要がある。)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
仮想マシンは、次の標準オプションのセットを認識する必要があります。
オプション文字列 説明 -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
」
または下線 (「_
」) で始まる必要があります。たとえば、Java 2 SDK は -Xms
および -Xmx
オプションをサポートしているため、プログラマは初期および最大のヒープサイズを指定できます。「-X
」
で始まるオプションは、「java
」コマンド行からアクセス可能です。
次の例は、JDK で 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, there is no longer any need to call
* JNI_GetDefaultJavaVMInitArgs.
*/
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res < 0) ...
JDK では、JDK 1.1 と厳密に同じ方法で JDK1_1InitArgs
をサポートしています。
Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。
p_vm
: 結果の VM 構造体が配置される位置へのポインタ
p_env
: メインスレッドの JNI
インタフェースポインタが配置される位置へのポインタ
vm_args
: Java VM 初期化引数
成功の場合は「0」を、失敗の場合は負の値を返します。
jint DestroyJavaVM(JavaVM *vm);
Java VM をアンロードし、そのリソースを回復します。メインスレッドだけが VM をアンロードできます。システムは、メインスレッドだけがユーザスレッドとして残るまで待機し、そのあと VM を破棄します。
JDK 1.1 では、DestroyJavaVM
は完全にサポートされているわけではありません。メインスレッドだけが DestroyJavaVM
を呼び出すことができます。Java 2 SDK
では、接続されているかいないかに関わらず、どのスレッドもこの関数を呼び出すことができます。現在のスレッドが接続されている場合、VM
は、現在のスレッドが唯一のユーザレベル Java スレッドになるまで待機します。現在のスレッドが接続されていない場合は、VM
が現在のスレッドを接続し、現在のスレッドが唯一のユーザレベルのスレッドになるまで待機します。ただし、VM のアンロードは、JDK
でもサポートされません。DestroyJavaVM
は、常にエラーコードを返します。
JavaVM インタフェース関数テーブルのインデックス 3
vm
: 破壊される Java VM
成功の場合は「0」を、失敗の場合は負の値を返します。
JDK 1.1.2 は VM のアンロードをサポートしません。
jint AttachCurrentThread(JavaVM
*vm, JNIEnv **p_env, void *thr_args);
現在のスレッドを Java VM へ接続します。JNIEnv
引数で JNI
インタフェースポインタを返します。
すでに接続されているスレッドへの接続は、無操作です。
ネイティブスレッドを 2 つの Java VM へ同時に接続することはできません。
スレッドが VM に接続されている場合、コンテキストクラスのローダは、ブートストラップローダです。
JavaVM インタフェース関数テーブルのインデックス 4
vm
: 現在のスレッドが接続される VM
p_env
: 現在のスレッドの JNI
インタフェースポインタが配置される位置へのポインタ
thr_args
: VM 固有のスレッド接続引数
JDK 1.1 で、AttachCurrentThread
への 2
番目の引数は、常に JNIEnv
へのポインタです。AttachCurrentThread
への 3 番目の引数は予約されており、null
に設定しなければなりません。
JDK で 1.1 の動作をさせる場合には、3 番目の引数として null
を渡します。または次の構造体にポインタを渡して、追加情報を指定することができます。
typedef struct JavaVMAttachArgs {
jint version; /* must be 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
成功の場合は「0」を、失敗の場合は負の値を返します。
jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);
AttachCurrentThread とセマンティクスは同じですが、新しく作成されれた java.lang.Thread インスタンスは「デーモン」です。
スレッドがすでに AttachCurrentThread または AttachCurrentThreadAsDaemon を介して接続されている場合、このルーチンは penv でポイントされている値を現在のスレッドの JNIEnv に設定します。この場合、AttachCurrentThread もこのルーチンも、スレッドの「デーモン」に影響しません。
JavaVM インタフェース関数テーブルのインデックス 7
vm: 現在のスレッドが接続される仮想マシンインスタンス
penv: 現在のスレッドの JNIEnv インタフェースポインタが配置される位置へのポインタ
args: JavaVMAttachArgs 構造体へのポインタ
成功した場合は 0、失敗した場合は負の数を返します。
なし
JDK/JRE 1.4
jint DetachCurrentThread(JavaVM *vm);
Java VM から現在のスレッドを分離します。このスレッドが保持する Java モニターはすべて解放されます。このスレッドが終了するのを待つ Java スレッドすべてに、通知が行われます。
JDK 1.1 では、VM からメインスレッドを切り離すことはできません。VM 全体をアンロードするには、DestroyJavaVM
を呼び出す必要があります。
JDK では、VM からメインスレッドを切り離せます。
Java VM を作成するスレッドであるメインスレッドを、VM
から分離することができません。その代わり、VM 全体をアンロードするために、メインスレッドは JNI_DestroyJavaVM()
を呼び出す必要があります。
JavaVM インタフェース関数テーブルのインデックス 5
vm
: 現在のスレッドが分離される VM
成功の場合は「0」を、失敗の場合は負の値を返します。
現在のスレッドが VM に接続されていない場合、*env
を null
に設定し、JNI_EDETACHED
を返します。指定したバージョンがサポートされていない場合、*env
を null
に設定し、JNI_EVERSION
を返します。それ以外の場合は、*env
を適切なインタフェースに設定し、JNI_OK
を返します。
JDK/JRE 1.2
1. JDK 1.1 では、ネイティブコードでバージョンフィールドを設定する必要はありませんでした。下位互換のため、バージョンフィールドが設定されていない場合は、 JDK 1.1.2 は、要求されたバージョンが 0x00010001 だと想定します。JDK の将来のバージョンでは、バージョンフィールドを適切な値に設定する必要があります。
2. 詳細は、脚注 1 を参照してください。
目次 | 前の項目 | |
Copyright © 2003 Sun Microsystems, Inc. All rights reserved.