呼び出し API第 5 章この呼び出し API により、ソフトウェアベンダーは Java VM を任意のネイティブアプリケーションにロードできるようになります。そのベンダーの提供する Java が実行可能なアプリケーションは、Java VM ソースコードにリンクする必要がありません。 この章では、呼び出し API の概要の説明から始めます。それ以降は、すべての呼び出し API 関数のリファレンスページです。 Java VM の組み込み機能を向上させるため、JDK 1.1.2 の呼び出し API はいくつかの細かな点が拡張されています。 概要次のコード例では、呼び出し API の関数の使用方法について説明します。この例では、C++ コードは Java VM を生成し、 #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 埋め込みインタフェースに似ています。 VM の生成
VM への接続JNI インタフェースポインタ ( 接続されたスレッドには、適当な作業量を実行するための十分なスタック領域が必要です。スレッドごとのスタック領域の割り当ては、オペレーティングシステム固有です。たとえば、pthread を使用する場合は、 VM のアンロードメインスレッドは、VM から分離できません。代わりに、VM 全体をアンロードするために VM は、メインスレッドが唯一のユーザースレッドになるまで待機し、そのあとアンロードを実行します。ユーザースレッドには、Java スレッドおよび接続されたネイティブスレッドの両方があります。この制限は、Java スレッドまたは接続されたネイティブスレッドがロック、ウィンドウなどのシステムリソースを保持している可能性があるために存在します。 初期化構造体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 へ接続するために引数は必要ありません。 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; ライブラリおよびバージョン管理JDK 1.1 では、ネイティブライブラリを一度ロードすると、すべてのクラスローダーからそのライブラリを認識できました。そのため、異なるクラスローダーの 2 つのクラスが、同じネイティブメソッドにリンクしてしまう可能性がありました。このため次の 2 つの問題が発生します。
Java 2 SDK では、各クラスローダーは、独自のネイティブライブラリのセットを管理します。同じ JNI ネイティブライブラリを、2 つ以上のクラスローダーにロードすることはできません。そのようなことを行うと、
バージョン管理およびリソース管理を容易にするために、Java 2 プラットフォームの JNI ライブラリは次の 2 つの関数をオプションでエクスポートできます。 JNI_OnLoad
System.loadLibrary を介して) ネイティブライブラリがロードされると、VM は JNI_OnLoad を呼び出します。JNI_OnLoad は、ネイティブライブラリが必要とする JNI バージョンを返さなければなりません。
新しい JNI 関数のどれかを使用するために、ネイティブライブラリは リンケージ:ネイティブメソッド実装を含むネイティブライブラリからエクスポートされます。 SDK/JRE 1.4 以降:JDK 1.1 で使用可能であった JNI 関数に加え、J2SE リリース 1.2 で導入された JNI 関数を使用するには、ネイティブライブラリは リリース 1.2 で利用できた JNI 関数に加え、J2SE リリース 1.4 に導入された JNI 関数を使用する場合、ネイティブライブラリは ネイティブライブラリが JNI_OnUnload
JNI_OnUnload を呼び出します。この関数は、クリーンアップオペレーションに使用されます。これは未確認のコンテキスト (ファイナライザからのコンテキストなど) で呼び出される関数なので、プログラマは慎重に Java VM サービスを使用する必要があります。また Java コールバックを任意に行うことのないようにしなければなりません。
リンケージ:ネイティブメソッド実装を含むネイティブライブラリからエクスポートされます。呼び出し API の関数
typedef const struct JNIInvokeInterface *JavaVM; const struct JNIInvokeInterface ... = { NULL, NULL, NULL, DestroyJavaVM, AttachCurrentThread, DetachCurrentThread, GetEnv, AttachCurrentThreadAsDaemon }; 3 つの呼び出し API 関数 JNI_GetDefaultJavaVMInitArgs
Java VM のデフォルト構成を返します。この関数を呼び出す前に、ネイティブコードは vm_args->version フィールドを、VM でサポートされると予測される JNI バージョンに設定する必要があります。1JDK 1.1.2 では、vm_args->version は 0x00010001 に設定する必要があります。この関数から復帰すると、vm_args->version は、VM がサポートする実際の JNI バージョンに設定されます。 リンケージ:Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。 パラメータ:
戻り値:要求されたバージョンがサポートされている場合は「0」を返し、要求されたバージョンがサポートされていない場合は負の数を返します。 JNI_GetCreatedJavaVMs
作成された Java VM をすべて返します。VM へのポインタは、作成された順にバッファー vmBuf に書き込まれます。しかし、エントリの bufLen 番号しか書き込みません。作成された VM の全体数は、*nVM で返します。 JDK 1.1.2 は 1 つのプロセスで、1 つの VM しかサポートしません。 リンケージ:Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。 パラメータ:
戻り値:成功した場合は「0」を返し、失敗した場合は負の数を返します。 JNI_CreateJavaVM
ロードして、Java VM を初期化します。現在のスレッドがメインスレッドになります。 JDK 1.1 は 1 つのプロセスで、1 つの VM しかサポートしません。vm_args のバージョンフィールドは、0x00010001 に設定する必要があります。2 JDK 1.1 では、 Java 2 SDK では、標準 VM 初期化構造が導入されます。下位互換性は保持されます。VM 初期化引数が typedef struct JavaVMInitArgs { jint version; jint nOptions; JavaVMOption *options; jboolean ignoreUnrecognized; } JavaVMInitArgs;
typedef struct JavaVMOption { char *optionString; void *extraInfo; } JavaVMOption; 配列のサイズは、
加えて、各 VM 実装は、標準でない独自のオプション文字列のセットをサポートします。標準でないオプション名は、「 次の例は、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 と厳密に同じ方法で リンケージ:Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。 パラメータ:
戻り値:成功した場合は「0」を返し、失敗した場合は負の数を返します。 DestroyJavaVM
Java VM をアンロードし、そのリソースを回復します。メインスレッドだけが VM をアンロードできます。システムは、メインスレッドだけがユーザースレッドとして残るまで待機し、そのあと VM を破棄します。 1.1 では、 リンケージ:JavaVM インタフェース関数テーブルのインデックス 3。 パラメータ:
戻り値:成功した場合は「0」を返し、失敗した場合は負の数を返します。 JDK 1.1.2 は VM のアンロードをサポートしません。 AttachCurrentThread
現在のスレッドを Java VM へ接続します。 すでに接続されているスレッドへの接続は、無操作です。 ネイティブスレッドを 2 つの Java VM へ同時に接続することはできません。 スレッドが VM に接続されている場合、コンテキストクラスのローダーは、ブートストラップローダーです。 リンケージ:JavaVM インタフェース関数テーブルのインデックス 4。 パラメータ:
JDK 1.1 では、 Java 2 SDK では、3 番目の引数として 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」を返し、失敗した場合は負の数を返します。 AttachCurrentThreadAsDaemonjint 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 DetachCurrentThread
Java VM から現在のスレッドを分離します。このスレッドが保持する Java モニターはすべて解放されます。このスレッドが終了するのを待つ Java スレッドすべてに、通知が行われます。 JDK 1.1 では、VM からメインスレッドを切り離すことはできません。VM 全体をアンロードするには、 Java 2 SDK では、VM からメインスレッドを切り離すことができます。 Java VM を作成するスレッドであるメインスレッドを、VM から分離できません。その代わり VM 全体をアンロードするために、メインスレッドは リンケージ:JavaVM インタフェース関数テーブルのインデックス 5。 パラメータ:
戻り値:成功した場合は「0」を返し、失敗した場合は負の数を返します。 戻り値:現在のスレッドが VM に接続されていない場合は、 パラメータ:
1. JDK 1.1 では、ネイティブコードでバージョンフィールドを設定する必要はありませんでした。下位互換のため、バージョンフィールドが設定されていない場合は、JDK 1.1.2 は、要求されたバージョンが 0x00010001 だと想定します。JDK の将来のバージョンでは、バージョンフィールドを適切な値に設定する必要があります。 2. 詳細は、脚注 1 を参照してください。
|