バージョン 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
」で始まる必要がある。vfprintf
extraInfo
は、vfprintf
フックへのポインタexit
extraInfo
は、exit
フックへのポインタabort
extraInfo
は、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 |
|