目次||次

第5章: 呼出しAPI

この呼び出し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 10 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_10;
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をアンロードします。

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

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

ネイティブ・ライブラリは、動的にリンクされるか、またはVMと静的にリンクされます。 ライブラリとVMイメージを組み合せる方法は、実装に依存します。 System.loadLibraryまたはそれに相当するAPIは、ライブラリがロードされているとみなされると成功する必要があります。これは、動的リンク・ライブラリと静的リンク・ライブラリの両方に適用されます。

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

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

静的リンク・ライブラリのサポート

'L'という名前のこれらの例で示されている静的にリンクされたライブラリの場合、静的にリンクされたライブラリには次の規則が適用されます:

プログラマはJNI関数RegisterNatives()を呼び出して、クラスと関連付けられたネイティブ・メソッドを登録することもできます。 RegisterNatives()関数は、静的にリンクされた関数を使用する場合に特に有用です。

動的リンク・ライブラリがJNI_OnLoad_Lおよび/またはJNI_OnUnload_L関数を定義する場合、これらの関数は無視されます。

ライブラリ・ライフサイクル関数フック

バージョン管理とリソース管理を容易にするために、JNIライブラリはloadunload関数フックを定義することができます。 これらの関数の命名は、ライブラリが動的にリンクされているか静的にリンクされているかによって異なります。


JNI_OnLoad

jint JNI_OnLoad(JavaVM *vm, void *reserved);

動的にリンクされたライブラリによって定義されるオプションの関数。 (たとえばSystem.loadLibraryを介して)ネイティブ・ライブラリがロードされると、VMはJNI_OnLoadを呼び出します。

JNI APIの特定のバージョンで定義された関数を使用するには、JNI_OnLoadは少なくともそのバージョンを定義する定数を返す必要があります。 たとえば、JDK 1.4で導入されたAttachCurrentThreadAsDaemon関数を使用したいライブラリは、少なくともJNI_VERSION_1_4を返す必要があります。 ネイティブ・ライブラリがJNI_OnLoad関数をエクスポートしない場合、VMはライブラリがJNIバージョンJNI_VERSION_1_1を要求しているだけであるとみなします。 VMがJNI_OnLoadによって返されるバージョン番号を認識しない場合、VMはライブラリをアンロードし、ライブラリがロードされていないかのように動作します。

リンケージ:

ネイティブ・メソッドの実装を含む、動的にリンクされたネイティブ・ライブラリからエクスポートされます。

パラメータ:

vm: 現在のVM構造体へのポインタ。

reserved: 未使用ポインタ。

戻り値:

必要なJNI_VERSION定数(GetVersionも参照してください)を返します。


JNI_OnUnload

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

動的にリンクされたライブラリによって定義されるオプションの関数。 ネイティブ・ライブラリを含むクラス・ローダーのガベージ・コレクションの際に、VMはJNI_OnUnloadを呼び出します。

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

リンケージ:

ネイティブ・メソッドの実装を含む、動的にリンクされたネイティブ・ライブラリからエクスポートされます。

パラメータ:

vm: 現在のVM構造体へのポインタ。

reserved: 未使用ポインタ。


JNI_OnLoad_L

jint JNI_Onload_<L>(JavaVM *vm, void *reserved);

その必須機能は静的にリンクされたライブラリによって定義する必要があります

'L'という名前のライブラリが静的にリンクされている場合、System.loadLibrary("L")または同等のAPIの最初の呼び出し時に、JNI_OnLoad関数で指定されたものと同じ引数および期待される戻り値でJNI_OnLoad_L関数が呼び出されます。 JNI_OnLoad_Lは、ネイティブ・ライブラリが必要とするJNIバージョンを返さなければなりません。 このバージョンはJNI_VERSION_1_8以降である必要があります。 VMがJNI_OnLoad_Lによって返されたバージョン番号を認識しない場合、VMはライブラリがロードされなかったかのように動作します。

リンケージ:

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

パラメータ:

vm: 現在のVM構造体へのポインタ。

reserved: 未使用ポインタ。

戻り値:

必要なJNI_VERSION定数(GetVersionも参照してください)を返します。 返される最小バージョンは少なくともJNI_VERSION_1_8です。

導入されたバージョン:

JDK/JRE 1.8


JNI_OnUnload_L

void JNI_OnUnload_<L>(JavaVM *vm, void *reserved);

静的にリンクされたライブラリによって定義されるオプションの関数。 静的にリンクされたネイティブ・ライブラリ'L'を含むクラス・ローダーがガベージ・コレクトされると、そのような関数がエクスポートされると、VMはライブラリのJNI_OnUnload_L関数を呼び出します。

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

リンケージ:

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

パラメータ:

vm: 現在のVM構造体へのポインタ。

reserved: 未使用ポインタ。

導入されたバージョン:

JDK/JRE 1.8

情報ノート:

ネイティブ・ライブラリをロードする動作は、ライブラリとそのネイティブ・エントリ・ポイントをJava VMおよびランライムに認識させ、登録する完全なプロセスです。 単に、オペレーティング・システム・レベルの操作(UNIX(R)システムでのdlopenなど)を実行してネイティブ・ライブラリをロードするだけでは、この目標は完全には実現されないことに注意してください。 通常、ネイティブ関数は、ライブラリをメモリーにロードして、ネイティブ・ライブラリにハンドルを返すホスト・オペレーティング・システムの呼出しを実行するために、Javaクラス・ローダーから呼び出されます。 このハンドルは、以降のネイティブ・ライブラリのエントリ・ポイント検索の際に格納および使用されます。 ライブラリを登録するためにハンドルが正常に返されると、Javaネイティブ・クラス・ローダーでロード・プロセスが完了します。


呼び出し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構造体へのポインタは、NULLであってはなりません。

戻り値:

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


JNI_GetCreatedJavaVMs

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

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

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

リンケージ:

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

パラメータ:

vmBuf: VM構造が配置されるバッファへのポインタは、NULLであってはなりません。

bufLen: バッファの長さ。

nVMs: 整数を指すポインタ。 NULL値の可能性があります。

戻り値:

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


JNI_CreateJavaVM

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

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

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

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

typedef struct JavaVMInitArgs {
    jint version;

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

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構造体が配置されるロケーションへのポインタは、NULLであってはなりません。

p_env: メイン・スレッドのJNIインタフェース・ポインタが配置されるロケーションへのポインタは、NULLであってはなりません。

vm_args: Java VMの初期化引数は、NULLであってはなりません。

戻り値:

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


DestroyJavaVM

jint DestroyJavaVM(JavaVM *vm);

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

どのスレッドでも、接続されているかどうかにかかわらず、この関数を呼び出すことができます。 現在のスレッドが接続されている場合、VMは、現在のスレッドがデーモンでない唯一のユーザー・レベルJavaスレッドになるまで待機します。 現在のスレッドが接続されていない場合は、VMが現在のスレッドを接続し、現在のスレッドがデーモンでない唯一のユーザー・レベルのスレッドになるまで待機します。

リンケージ:

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

パラメータ:

vm: 破棄されるJava VMは、NULLであってはなりません。

戻り値:

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

VMのアンロードはサポートされていません。


AttachCurrentThread

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

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

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

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

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

リンケージ:

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

パラメータ:

vm: 現在のスレッドがアタッチされるVMは、NULLであってはなりません。

p_env: 現在のスレッドのJNIインタフェース・ポインタが配置されるロケーションへのポインタは、NULLであってはなりません。

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

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

追加情報を指定するには、次の構造体にポインタを渡します。

typedef struct JavaVMAttachArgs {
    jint version;
    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: 現在のスレッドがアタッチされる仮想マシン・インスタンスは、NULLであってはなりません。

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

args: JavaVMAttachArgs構造体へのポインタ。 NULL値の可能性があります。

戻り値

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


DetachCurrentThread

jint DetachCurrentThread(JavaVM *vm);

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

VMからメイン・スレッドを切り離すことができます。

アタッチされていないスレッドを切り離そうとすると、何もしません。

DetachCurrentThreadが呼び出されたときに例外が保留されている場合、VMはその存在を報告することを選択できます。

リンケージ:

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

パラメータ:

vm: 現在のスレッドが切り離されるVMは、NULLであってはなりません。

戻り値:

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


GetEnv

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

リンケージ:

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

パラメータ:

vm: インタフェースを取得する仮想マシン・インスタンスは、NULLであってはなりません。

env: 現在のスレッドのJNIインタフェース・ポインタが配置されるロケーションへのポインタは、NULLであってはなりません。

version: リクエストされたJNIバージョン。

戻り値:

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


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

目次||次