目次 | 前へ

呼び出し API


第 5 章

この呼び出し API により、ソフトウェアベンダーは Java VM を任意のネイティブアプリケーションにロードできるようになります。そのベンダーの提供する Java が実行可能なアプリケーションは、Java VM ソースコードにリンクする必要がありません。

この章では、呼び出し API の概要の説明から始めます。それ以降は、すべての呼び出し 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 */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/usr/lib/java";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* 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 の生成

JNI_CreateJavaVM() 関数は Java VM をロードして初期化し、JNI インタフェースポインタへのポインタを返します。JNI_CreateJavaVM() を呼び出したスレッドは、メインスレッドとみなされます。

VM への接続

JNI インタフェースポインタ (JNIEnv) は、現在のスレッドでのみ有効です。別のスレッドが Java VM にアクセスする必要がある場合、これは最初に AttachCurrentThread() を呼び出して、自身を VM に接続し JNI インタフェースポインタを取得する必要があります。一度 VM に接続されると、ネイティブスレッドはネイティブメソッド内で実行中の普通の Java スレッドのように機能します。ネイティブスレッドは、DetachCurrentThread() を呼び出して自身を切り離すまで VM に接続されたままになります。

接続されたスレッドには、相当量の作業を実行するのに十分なスタック領域が必要です。スレッドごとのスタック領域の割り当ては、オペレーティングシステム固有です。たとえば、pthread を使用する場合は、pthread_create への pthread_attr_t 引数でスタックサイズを指定できます。

VM からの分離

VM に接続されたネイティブスレッドは、終了する前に DetachCurrentThread() を呼び出して自身を分離する必要があります。呼び出しスタック上に Java メソッドがある場合、スレッドは分離できません。

VM のアンロード

JNI_DestroyJavaVM()関数は、Java VM をアンロードします。JDK/JRE 1.1 では、DestroyJavaVM を呼び出して VM をアンロードできるのはメインスレッドだけでした。JDK/JRE 1.2 では、この制限が削除され、どのスレッドでも DestroyJavaVM を呼び出して VM をアンロードできます。

VM は、現在のスレッドを実際にアンロードする前に、それがデーモンでない唯一のユーザースレッドになるまで待機します。ユーザースレッドには、Java スレッドおよび接続されたネイティブスレッドの両方があります。この制限は、Java スレッドまたは接続されたネイティブスレッドがロック、ウィンドウなどのシステムリソースを保持している可能性があるために存在します。VM は、自動的にこれらのリソースを解放することはできません。VM がアンロードされているときに、現在のスレッドを実行中の唯一のスレッドに制限することにより、任意のスレッドが保持しているシステムリソースを解放する負荷はプログラマに課せられます。

ライブラリおよびバージョン管理

JDK/JRE 1.1 では、ネイティブライブラリを一度ロードすると、すべてのクラスローダーからそのライブラリを認識できました。そのため、異なるクラスローダーの 2 つのクラスが、同じネイティブメソッドにリンクしてしまう可能性がありました。このため次の 2 つの問題が発生します。

JDK/JRE 1.2 では、クラスローダーが各自のネイティブライブラリのセットを管理します。同じ JNI ネイティブライブラリを、2 つ以上のクラスローダーにロードすることはできません。そのようなことを行うと、UnsatisfiedLinkError がスローされます。たとえば、System.loadLibrary を使用して 2 つのクラスローダーにネイティブライブラリをロードしようとすると、UnsatisfiedLinkError がスローされます。この新しい手法の利点は次のとおりです。

バージョン管理およびリソース管理を容易にするために、JDK/JRE 1.2 の JNI ライブラリは次の 2 つの関数をオプションでエクスポートできます。

JNI_OnLoad

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/JRE 1.4

JDK/JRE 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 によって返されたバージョン番号を認識しない場合、ネイティブライブラリをロードすることはできません。

JNI_OnUnload

void JNI_OnUnload(JavaVM *vm, void *reserved);

ネイティブライブラリを含むクラスローダーのガベージコレクションの際に、VM は JNI_OnUnload を呼び出します。この関数は、クリーンアップオペレーションに使用されます。これは未確認のコンテキスト (ファイナライザからのコンテキストなど) で呼び出される関数なので、プログラマは慎重に Java VM サービスを使用する必要があります。また Java コールバックを任意に行うことのないようにしなければなりません。

JNI_OnLoadJNI_OnUnload は、JNI ライブラリがオプションで提供する 2 つの関数であり、VM からエクスポートされるものではありません。

リンケージ:

ネイティブメソッド実装を含むネイティブライブラリからエクスポートされます。

呼び出し API の関数

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 構造体がなくても使用できます。

JNI_GetDefaultJavaVMInitArgs

jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

Java VM のデフォルト構成を返します。この関数を呼び出す前に、ネイティブコードは vm_args->version フィールドを、VM でサポートされると予測される JNI バージョンに設定する必要があります。この関数から復帰すると、vm_args->version は、VM がサポートする実際の JNI バージョンに設定されます。

リンケージ:

Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。

パラメータ:

vm_args: デフォルト引数が入る JavaVMInitArgs 構造体を指すポインタ。

戻り値:

要求されたバージョンがサポートされている場合は JNI_OK を返し、要求されたバージョンがサポートされていない場合は JNI エラーコード (負の数) を返します。

JNI_GetCreatedJavaVMs

jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

作成された Java VM をすべて返します。VM へのポインタは、作成された順にバッファー vmBuf に書き込まれます。しかし、エントリの bufLen 番号しか書き込みません。作成された VM の全体数は、*nVM で返します。

JDK/JRE 1.2 では、1 つのプロセスでの複数の VM の作成はサポートされていません。

リンケージ:

Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。

パラメータ:

vmBuf: VM 構造体が配置されるバッファーへのポインタ。

bufLen: バッファーの長さ。

nVMs: 整数を指すポインタ。

戻り値:

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

JNI_CreateJavaVM

jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

ロードして、Java VM を初期化します。現在のスレッドがメインスレッドになります。env 引数を、メインスレッドの JNI インタフェースポインタに設定します。

JDK/JRE 1.2 では、1 つのプロセスでの複数の VM の作成はサポートされていません。

JNI_CreateJavaVM の 2 番目の引数が常に JNIEnv * を参照するポインタであるのに対して、3 番目の引数は、オプション文字列を使用してすべての VM 起動オプションをエンコードする JavaVMInitArgs 構造体を参照するポインタです。

typedef struct JavaVMInitArgs {
    jint version;

    jint nOptions;
    JavaVMOption *options;
    jboolean ignoreUnrecognized;
} JavaVMInitArgs;

version フィールドは、少なくとも JNI_VERSION_1_2 に設定する必要があります。options フィールドは、次の型の配列です。

typedef struct JavaVMOption {
    char *optionString;  /* the option as a string in the default platform encoding */
    void *extraInfo;
} JavaVMOption;

配列のサイズは、JavaVMInitArgs の nOptions フィールドに示されます。ignoreUnrecognizedJNI_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 とクラスローディング関連のメッセージを出力するように指示する。標準的な名前には、gcclass、および jni。標準でない (VM 固有の) 名前はすべて、「X」で始まる必要がある。
vfprintf extraInfo は、vfprintf フックへのポインタ。
exit extraInfo は、exit フックへのポインタ。
abort extraInfo は、abort フックへのポインタ。

 

加えて、各 VM 実装は、標準でない独自のオプション文字列のセットをサポートします。標準でないオプション名は、「-X」または下線 (「_」) で始まる必要があります。たとえば、JDK/JRE は -Xms および -Xmx オプションをサポートしているため、プログラマは初期および最大のヒープサイズを指定できます。「-X」で始まるオプションは、「java」コマンド行からアクセス可能です。

次の例は、JDK/JRE で 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/JRE, there is no longer any need to call
 * JNI_GetDefaultJavaVMInitArgs.
 */
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res < 0) ...

リンケージ:

Java 仮想マシンを実装するネイティブライブラリからエクスポートされます。

パラメータ:

p_vm: 結果の VM 構造体が配置される位置へのポインタ。

p_env: メインスレッドの JNI インタフェースポインタが配置される位置へのポインタ。

vm_args: Java VM 初期化引数。

戻り値:

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

DestroyJavaVM

jint DestroyJavaVM(JavaVM *vm);

Java VM をアンロードし、そのリソースを回復します。

JDK/JRE 1.1 では、DestroyJavaVM は完全にサポートされているわけではありません。JDK/JRE 1.1 では、メインスレッドだけが DestroyJavaVM を呼び出すことができます。 JDK/JRE 1.2 以降、どのスレッドでも、接続されているかどうかにかかわらず、この関数を呼び出せます。現在のスレッドが接続されている場合、VM は、現在のスレッドがデーモンでない唯一のユーザーレベル Java スレッドになるまで待機します。現在のスレッドが接続されていない場合は、VM が現在のスレッドを接続し、現在のスレッドがデーモンでない唯一のユーザーレベルのスレッドになるまで待機します。ただし、VM のアンロードは、JDK/JRE でもサポートされません。

リンケージ:

JavaVM インタフェース関数テーブルのインデックス 3。

パラメータ:

vm: 破棄される Java VM。

戻り値:

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

JDK/JRE 1.1.2 では、VM のアンロードはサポートされていません。

AttachCurrentThread

jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);

現在のスレッドを Java VM へ接続します。JNIEnv 引数で JNI インタフェースポインタを返します。

すでに接続されているスレッドへの接続は、無操作です。

ネイティブスレッドを 2 つの Java VM へ同時に接続することはできません。

スレッドが VM に接続されている場合、コンテキストクラスのローダーは、ブートストラップローダーです。

リンケージ:

JavaVM インタフェース関数テーブルのインデックス 4。

パラメータ:

vm: 現在のスレッドが接続される VM。

p_env: 現在のスレッドの JNI インタフェースポインタが配置される位置へのポインタ。

thr_args: NULL または JavaVMAttachArgs 構造体を参照するポインタにして、追加情報を指定できます。

JDK/JRE 1.1 では、AttachCurrentThread の 2 番目の引数は常に JNIEnv を参照するポインタです。AttachCurrentThread への 3 番目の引数は予約されており、NULL に設定しなければなりません。

JDK/JRE 1.2 では、1.1 の動作をさせるには 3 番目の引数として NULL を渡します。または、次の構造体にポインタを渡して追加情報を指定できます。

typedef struct JavaVMAttachArgs {
    jint version;  /* must be at least 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

戻り値:

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

AttachCurrentThreadAsDaemon

jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);

AttachCurrentThread とセマンティクスは同じですが、新しく作成された java.lang.Thread インスタンスはデーモンです。

スレッドがすでに AttachCurrentThread または AttachCurrentThreadAsDaemon を介して接続されている場合、このルーチンは、penv が指している値を現在のスレッドの JNIEnv に設定します。この場合、AttachCurrentThread もこのルーチンも、スレッドのデーモンステータスに影響しません。

リンケージ:

JavaVM インタフェース関数テーブルのインデックス 7。

パラメータ:

vm: 現在のスレッドが接続される仮想マシンインスタンス。

penv: 現在のスレッドの JNIEnv インタフェースポインタが配置される位置へのポインタ。

args: JavaVMAttachArgs 構造体へのポインタ。

戻り値

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

例外

なし。

導入されたバージョン:

JDK/JRE 1.4

DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

Java VM から現在のスレッドを分離します。このスレッドが保持する Java モニターはすべて解放されます。このスレッドが終了するのを待つ Java スレッドすべてに、通知が行われます。

JDK/JRE 1.2 では、VM からメインスレッドを切り離すことができます。

リンケージ:

JavaVM インタフェース関数テーブルのインデックス 5。

パラメータ:

vm: 現在のスレッドが分離される VM。

戻り値:

成功した場合は JNI_OK を返し、失敗した場合は該当する JNI エラーコード (負の数) を返します。

GetEnv

jint GetEnv(JavaVM *vm, void **env, jint version);

リンケージ:

JavaVM インタフェース関数テーブルのインデックス 6。

パラメータ:

vm: インタフェース取得元の仮想マシンインスタンス。
env: 現在のスレッドの JNI インタフェースポインタが配置される位置へのポインタ。
version: 要求された JNI バージョン。

戻り値:

現在のスレッドが VM に接続されていない場合は、*envNULL に設定し、JNI_EDETACHED を返します。指定されたバージョンがサポートされていない場合は、*envNULL に設定し、JNI_EVERSION を返します。それ以外の場合は、*env を適切なインタフェースに設定し、JNI_OK を返します。

導入されたバージョン:

JDK/JRE 1.2

 


目次 | 前へ

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.