|
バージョン 1.2 の JavaTM 2 SDK
|
JNI ドキュメント |
Java 2 SDK では、次の JNI の機能が拡張されています。
JNI に対する変更は、ユーザの方々からのフィードバックに基づいて行われます。 コメント、提案、関心のある点について、jni@java.sun.com までお知らせください。
#define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 /* Error codes */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error */
FindClass 関数が拡張され、クラスローダによってロードされたクラスを検索することができるようになりました。
jclass FindClass(JNIEnv *env, const char *name);
JDK 1.1 では、
FindClassはCLASSPATH内のローカルクラスだけを検索しました。 検索されたクラスは、クラスローダを持っていませんでした。Java セキュリティモデルは拡張され、システムクラス以外のクラスによるネイティブメソッドのロードおよび呼び出しが可能になりました。 Java 2 プラットフォームでは、
FindClassが、現在のネイティブメソッドと関連付けられているクラスローダを検出します。 ネイティブコードがシステムクラスに属する場合、クラスローダは検出されません。 それ以外の場合には、適切なクラスローダが呼び出され、名前が付けられたクラスのロードおよびリンクを行います。
FindClassが呼び出しインタフェースによって呼び出された場合、現在のネイティブメソッドまたはそれに関連付けられたクラスローダは存在しません。 この場合、ClassLoader.getBaseClassLoaderの結果が使用されます。 これは、仮想マシンがアプリケーション用に作成するクラスローダです。java.class.pathプロパティにリストされたクラスを検索できます。
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によって返されたバージョン番号を認識しない場合、ネイティブライブラリをロードすることはできません。
void JNI_OnUnload(JavaVM *vm, void *reserved);
ネイティブライブラリを含むクラスローダのガベージコレクションの際に、VM は JNI_OnUnload を呼び出します。 この関数は、クリーンアップオペレーションに使用されます。 これは未確認のコンテキスト (ファイナライザからのコンテキストなど) で呼び出される関数なので、プログラマは慎重に Java VM サービスを使用する必要があります。また Java コールバックを任意に行うことのないようにしなければなりません。
JNI_OnLoad と JNI_OnUnload は、JNI ライブラリがオプションで提供する 2 つの関数であり、VM からエクスポートされるものではありません。
JDK 1.1 は、DeleteLocalRef 関数を提供したため、プログラマは手動でローカル参照を削除することができました。 たとえば、ネイティブコードがオブジェクトの潜在的に大きな配列を繰り返しにより処理し、反復ごとに 1 つの要素を使用する場合、次の反復で新しいローカル参照が作成される前に、もう使用されない配列要素へのローカル参照を削除するのは良い方法です。
Java 2 SDK では、ローカル参照の有効期間の管理用に関数のセットが追加されました。
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);
現在のスレッドで最低限指定された数のローカル参照を作成できることを保証します。 関数が正常に実行された場合には、0 が返されます。それ以外の場合には、負の数が返され
OutOfMemoryErrorがスローされます。ネイティブメソッドに入る前に、VM は自動的に最低 16 のローカル参照の作成を保証します。
下位互換性のために VM は、保証された容量以上にローカル参照を割り当てます。 (デバッグのサポートとして、VM がユーザに対し、ローカル参照の作成数が多すぎるという内容の警告を発する場合があります。 Java 2 SDK では、プログラマは
-verbose:jniコマンド行オプションを供給し、これらのメッセージを有効にすることができます) 保証された容量を超えてしまい、これ以上ローカル参照を作成できない場合、VM は、FatalErrorを呼び出します。
jint PushLocalFrame(JNIEnv *env, jint capacity);
新しいローカル参照フレームを作成します。このフレームに最低限指定された数のローカル参照を作成できます。 正常に実行された場合には、0 が返されます。失敗した場合には、負の数が返され、
OutOfMemoryErrorがスローされます。前回のローカルフレームで作成済みのローカル参照は、現在のローカルフレームでも依然として有効です。
jobject PopLocalFrame(JNIEnv *env, jobject result);
現在のローカル参照フレームを無効にし、すべてのローカル参照を解放します。そして指定された
resultオブジェクトに対する前回のローカル参照フレームのローカル参照を返します。前回のフレームに参照を返す必要のない場合には、
resultとしてnullを渡します。
jobject NewLocalRef(JNIEnv *env, jobject ref);
refと同じオブジェクトを参照する新しいローカル参照を作成します。 渡されたrefは、グローバル参照またはローカル参照である可能性があります。refがnullを参照している場合には、nullが返されます。
jboolean ExceptionCheck(JNIEnv *env);
未処理の例外がある場合はJNI_TRUEを、ない場合はJNI_FALSEを返します。
null と同等です。 プログラマは、IsSameObject を使用して弱参照と null とを比較することにより、弱グローバル参照が解放されたオブジェクトを参照しているかどうか確認できます。
JNI の弱グローバル参照は、Java 2 プラットフォーム API (java.lang.ref およびそのクラス) の一部として入手可能な Java 弱参照の簡易版です。
解説 (2001年6月に追加)
ガベージコレクションはネイティブメソッドの実行中に発生するため、弱グローバル参照で参照されているオブジェクトはいつでも解放できます。 弱グローバル参照は、グローバル参照が使用されているところならどこでも使用できますが、そのように使用すると予告なしで
nullと機能的に同等になる場合があるので、一般的には不適切です。
IsSameObjectは弱グローバル参照が解放されたオブジェクトを参照しているかどうかを判別するのに使用できますが、オブジェクトがその直後に解放されるのを防止するわけではありません。 そのため、プログラマはこの検査に基づいて、その後の JNI 呼び出しで弱グローバル参照が (null参照以外で) 使用されているかどうかを判別することはできません。この継承制限を解消するため、JNI 関数
NewLocalRefまたはNewGlobalRefを使用して同一のオブジェクトへの標準 (強い) ローカル参照またはグローバル参照を取得し、この強い参照を使用して該当するオブジェクトにアクセスすることをお勧めします。 オブジェクトが解放されている場合、この関数はnullを返します。その他の場合、強い参照を返します (強い参照はオブジェクトが解放されるのを防止します)。 オブジェクトへの直接アクセスが不要になったときは、オブジェクトを解放できるように、新しい参照を明示的に削除する必要があります。弱グローバル参照は、他の種類の弱い参照 (SoftReference クラスまたは WeakReference クラスの Java オブジェクト) よりも弱い参照です。 特定のオブジェクトへの弱いグローバル参照は、そのオブジェクトを参照する SoftReference オブジェクトまたは WeakReference オブジェクトが参照を解除するまで、機能的に
nullと同等にはなりません。弱グローバル参照は、ファイナライズを必要とするオブジェクトへのJava の内部参照よりも弱い参照です。 弱グローバル参照は、参照先のオブジェクトのファイナライザが存在する場合、それが完了するまで、
nullと機能的に同等にはなりません。弱グローバル参照と PhantomReference との相互動作は未定義です。 特に、Java VM の実装は、PhantomReference のあとに弱グローバル参照を処理する場合があり、PhantomReference オブジェクトでも参照されているオブジェクトを保持するために弱グローバル参照を使用することが可能な場合があります。 弱グローバル参照をこのような未定義の方法で使用するのは避けるべきです。
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
弱グローバル参照を新規作成します。objがnullを参照している場合、または VM がメモリを使い果たしてしまった場合は、nullが返されます。 VM がメモリを使い果たしてしまった場合、OutOfMemoryErrorがスローされます。
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
渡された弱グローバル参照に必要な VM リソースを削除します。
JDK 1.1 では、プログラマは Get/Release<PrimitiveType>ArrayElements 関数を使用して、プリミティブ配列要素へのポインタを取得できました。 VM がピニングをサポートしている場合、元のデータへのポインタが返されました。 サポートしていない場合には、コピーが作成されました。
新しい関数を使用すると、VM がピニングをサポートしていない場合でも、ネイティブコードは配列要素への直接ポインタを取得することができます。
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);
上の 2 つの関数のセマンティクスは、既存のGet/Release<PrimitiveType>ArrayElements関数と非常によく似ています。 可能な場合は、VM はプリミティブ配列へのポインタを返します。そうでない場合は、コピーが作成されます。 ただし、これらの関数の使用方法に関して重要な制限があります。
GetPrimitiveArrayCriticalを呼び出したあと、ReleasePrimitiveArrayCriticalを呼び出す前に、ネイティブコードを特定の期間実行しないようにします。 この 1 組の関数内部のコードは「クリティカルリージョン」で実行されているものとして扱う必要があります。 クリティカルリージョン内部においてネイティブコードは、ほかの JNI 関数を呼び出してはならず、現在のスレッドにほかの Java スレッドをブロックして待機させることを可能にするどのシステムコールも呼び出してはなりません。 たとえば、現在のスレッドは、ほかの Java スレッドが記述したストリーム上のreadを呼び出してはなりません。これらの制限は、VM がピニングをサポートしない場合でも、ネイティブコードが配列のコピーされていないバージョンを取得する可能性を高めます。たとえば、ネイティブ コードが
GetPrimitiveArrayCriticalによって取得された配列へのポインタを保持している場合、VM は一時的にガベージコレクションを無効にすることがあります。
GetPrimtiveArrayCriticalおよびReleasePrimitiveArrayCriticalの複数の組み合わせは、入れ子にすることができます。 例を示します。jint len = (*env)->GetArrayLength(env, arr1); jbyte *a1 = (*env)->GetPrimitiveArrayCritical(env, arr1, 0); jbyte *a2 = (*env)->GetPrimitiveArrayCritical(env, arr2, 0); /* We need to check in case the VM tried to make a copy. */ if (a1 == NULL || a2 == NULL) { ... /* out of memory exception thrown */ } memcpy(a1, a2, len); (*env)->ReleasePrimitiveArrayCritical(env, arr2, a2, 0); (*env)->ReleasePrimitiveArrayCritical(env, arr1, a1, 0);
VM が内部的に異なる形式で配列を表す場合、GetPrimitiveArrayCritical はまだ配列のコピーを作成する可能性があります。 そのため、起こり得るメモリ不足の状況に対応して null に対する戻り値をチェックする必要があります。
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
オフセットstartで始まるlen個の Unicode 文字を、与えられたバッファbufにコピーします。
StringIndexOutOfBoundsExceptionをインデックスオーバーフロー時にスローします。
<
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
オフセットstartで始まるlen個の Unicode 文字を UTF-8 形式に変換し、その結果を与えられたバッファbufに置きます。
StringIndexOutOfBoundsExceptionをインデックスオーバーフロー時にスローします。
const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
上の 2 つの関数のセマンティクスは、既存のGet/ReleaseStringChars関数に似ています。 可能な場合には、VM は文字列要素へのポインタを返します。そうでない場合、コピーが作成されます。 ただし、これらの関数の使用方法に関して重要な制限があります。Get/ReleaseStringCriticalの呼び出しによって囲まれるコードセグメントにおいて、ネイティブコードは任意の JNI 呼び出しを行なったり、現在のスレッドにブロックさせてはなりません。
Get/ReleaseStringCriticalの制限は、Get/ReleasePrimitiveArrayCriticalの制限と類似しています。
プログラマは、メソッドまたはフィールドの名前および型を把握している場合、JNI を使用して Java メソッドの呼び出しまたは Java フィールドへのアクセスを行うことができます。 Java Core Reflection API を使用すると、プログラマは実行時に Java クラスの内部を調査することができます。 JNI は、JNI で使用されるフィールドとメソッド ID および Java Core Reflection API で使用されるメソッドオブジェクトの間の変換関数のセットを提供します。
jmethodID FromReflectedMethod(JNIEnv *env, jobject method);
java.lang.reflect.Methodまたはjava.lang.reflect.Constructorオブジェクトをメソッド ID に変換します。
jfieldID FromReflectedField(JNIEnv *env, jobject field);
java.lang.reflect.Field をフィールド ID に変換します。
jobject ToReflectedMethod(JNIEnv *env, jclass cls,
jmethodID methodID);
clsから取得したメソッド ID をjava.lang.reflect.Methodまたはjava.lang.reflect.Constructorオブジェクトに変換します。変換に失敗した場合には、
OutOfMemoryErrorをスローし、0 を返します。
jobject ToReflectedField(JNIEnv *env, jclass cls,
jfieldID fieldID);
clsから取得したフィールド ID をjava.lang.reflect.Fieldオブジェクトに変換します。変換に失敗した場合には、
OutOfMemoryErrorをスローし、0 を返します。
jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
JDK 1.1 では、JNI_CreateJavaVMへの 2 番目の引数は常にJNIEnv *へのポインタでした。 3 番目の引数は、JDK 1.1 に固有の構造体 (JDK1_1InitArgs) へのポインタです。JDK1_1InitArgs構造は、すべての VM に移植性のあるものとして設計されていないことは明らかです。J 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」または「_」で始まるすべての認識できないオプション文字列を無視します。 ignoreUnrecognizedJNI_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」で始まる必要がある。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をサポートしています。
jint AttachCurrentThread(JavaVM *vm, void **penv, void *args);
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;
jint DetachCurrentThread(JavaVM *vm);
JDK 1.1 では、VM からメインスレッドを切り離すことはできません。 VM 全体をアンロードするには、DestroyJavaVMを呼び出す必要があります。Java 2 SDK では、VM からメインスレッドを切り離すことができます。
jint DestroyJavaVM(JavaVM *vm);
JDK 1.1 では、DestroyJavaVMは完全にサポートされているわけではありません。 メインスレッドだけがDestroyJavaVMを呼び出すことができます。 Java 2 SDK では、接続されているかいないかに関わらず、どのスレッドもこの関数を呼び出すことができます。 現在のスレッドが接続されている場合、VM は、現在のスレッドが唯一のユーザレベル Java スレッドになるまで待機します。 現在のスレッドが接続されていない場合は、VM が現在のスレッドを接続し、現在のスレッドが唯一のユーザレベルのスレッドになるまで待機します。 ただし、VM のアンロードは、Java 2 SDK でもサポートされません。DestroyJavaVMは、常にエラーコードを返します。
jint GetEnv(JavaVM *vm, void **env, jint version);
現在のスレッドが VM に接続されていない場合、*envをnullに設定し、JNI_EDETACHEDを返します。 指定したバージョンがサポートされていない場合、*envをnullに設定し、JNI_EVERSIONを返します。 それ以外の場合は、*envを適切なインタフェースに設定し、JNI_OKを返します。
| Copyright © 1995-99 Sun Microsystems, Inc. All Rights Reserved. コメントの送付先: jni@java.sun.com |
|