| 目次 | 前の項目 | 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 がアンロードされているときに、メインスレッドを唯一の実行中のスレッドであると制限することで、任意のスレッドが保持するシステムリソースを解放する負荷はプログラマに課せられます。
Java VM によって異なる初期化引数が必要とされる可能性があります。現在および将来のすべての Java VM に対し、適切な、標準の初期化構造体を提供することは困難です。妥協策として、最初のフィールド (version) を初期化構造体の内容識別用に予約しておきます。JDK 1.1.2 を組み込んだネイティブアプリケーションは、このバージョンフィールドを 0x00010001 に設定する必要があります。実装によっては、JDK のサポートする初期化引数のいくつかを無視することを選択するかもしれませんが、VM 実装では、JDK と同じ初期化構造体を使うことをお勧めします。
バージョン番号 0x80000000 から 0xFFFFFFFF は予約されているので、VM 実装で使用できません。
次のコードは、JDK 1.1.2 で Java VM の初期化に使われる構造体を示します。
typedef struct JDK1_1InitArgs {
/* The first two fields were reserved in JDK 1.1, and
formally introduced in JDK 1.1.2. */
/* Java VM version */
jint version;
/* System properties. */
char **properties;
/* whether to check the Java source files are newer than
* compiled class files. */ */
jint checkSource;
/* maximum native stack size of Java-created threads. */ */
jint nativeStackSize;
/* maximum Java stack size. */ */
jint javaStackSize;
/* initial heap size. */ */
jint minHeapSize;
/* maximum heap size. */ */
jint maxHeapSize;
/* controls whether Java byte code should be verified:
* 0 -- none, 1 -- remotely loaded code, 2 -- all code. */
jint verifyMode;
/* the local directory path for class loading. */
const char *classpath;
/* a hook for a function that redirects all VM messages. */
jint (*vfprintf)(FILE *fp, const char *format,
va_list args);
/* a VM exit hook. */
void (*exit)(jint code);
/* a VM abort hook. */
void (*abort)();
/* whether to enable class GC. */
jint enableClassGC;
/* whether GC messages will appear. */
jint enableVerboseGC;
/* whether asynchronous GC is allowed. */
jint disableAsyncGC;
/* Three reserved fields. */
jint reserved0;
jint reserved1;
jint reserved2;
} JDK1_1InitArgs;
JDK 1.1.2 では、初期化構造体が提供するフックにより、ネイティブアプリケーションは VM メッセージをリダイレクトすることができ、VM の終了を制御することができるようになります。
次に示す構造体は、ネイティブスレッドが JDK 1.1.2 の Java VM に接続するとき、引数として渡されます。実際、ネイティブスレッドが JDK 1.1.2 へ接続するために引数は必要ありません。JDK1_1AttachArgs 構造体は、空の構造体を許さない C コンパイラ用の埋め込みスロットだけから成ります。
typedef struct JDK1_1AttachArgs {
/*
* JDK 1.1 does not need any arguments to attach a
* native thread.The padding is here to satisfy the C
* compiler which does not permit empty structures.
*/
void *__padding;
} JDK1_1AttachArgs;
Java 2 SDK では、各クラスローダは、独自のネイティブライブラリのセットを管理します。同じ 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 に移植性のあるものとして設計されていないことは明らかです。
Java 2 SDK では、標準 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;
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」で始まる必要があるvfprintfextraInfoは、vfprintfフックへのポインタexitextraInfoは、exitフックへのポインタabortextraInfoは、abortフックへのポインタ
加えて、各 VM 実装は、標準でない独自のオプション文字列のセットをサポートします。標準でないオプション名は、「-X」または下線 (「_」) で始まる必要があります。たとえば、Java 2 SDK は -Xms および -Xmx オプションをサポートしているため、プログラマは初期および最大のヒープサイズを指定できます。「-X」で始まるオプションは、「java」コマンド行からアクセス可能です。
次の例は、Java 2 SDK で 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 Java 2 SDK, there is no longer any need to call * JNI_GetDefaultJavaVMInitArgs. */ res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args); if (res < 0) ...
Java 2 SDK では、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 のアンロードは、Java 2 SDK でもサポートされません。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 に設定しなければなりません。
Java 2 SDK で 1.1 の動作をさせる場合には、3 番目の引数として null を渡します。または次の構造体にポインタを渡して、追加情報を指定することができます。
typedef struct JavaVMAttachArgs {
jint version; /* must be JNI_VERSION_1_2 */
char *name; /* the name of the thread, 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、失敗した場合は負の数を返します。
なし
SDK/JRE 1.4
jint DetachCurrentThread(JavaVM *vm);
Java VM から現在のスレッドを分離します。このスレッドが保持する Java モニターはすべて解放されます。このスレッドが終了するのを待つ Java スレッドすべてに、通知が行われます。
JDK 1.1 では、VM からメインスレッドを切り離すことはできません。VM 全体をアンロードするには、DestroyJavaVM を呼び出す必要があります。
Java 2 SDK では、VM からメインスレッドを切り離すことができます。
Java VM を作成するスレッドであるメインスレッドを、VM から分離することができません。その代わり、VM 全体をアンロードするために、メインスレッドは JNI_DestroyJavaVM() を呼び出す必要があります。
JavaVM インタフェース関数テーブルのインデックス 5
vm: 現在のスレッドが分離される VM
成功の場合は「0」を、失敗の場合は負の値を返します。
現在のスレッドが VM に接続されていない場合、*env を null に設定し、JNI_EDETACHED を返します。指定したバージョンがサポートされていない場合、*env を null に設定し、JNI_EVERSION を返します。それ以外の場合は、*env を適切なインタフェースに設定し、JNI_OK を返します。
SDK/JRE 1.2
1. JDK 1.1 では、ネイティブコードでバージョンフィールドを設定する必要はありませんでした。下位互換のため、バージョンフィールドが設定されていない場合は、JDK 1.1.2 は、要求されたバージョンが 0x00010001 だと想定します。JDK の将来のバージョンでは、バージョンフィールドを適切な値に設定する必要があります。
2. 詳細は、脚注 1 を参照してください。
| 目次 | 前の項目 |
Copyright © 2003 Sun Microsystems, Inc. All rights reserved.