1 - 入門
この章では、Java Native Interface (JNI) を紹介します。JNI は、ネイティブプログラミングインタフェースです。これによって、Java 仮想マシン* (VM) で実行される Java コードが C、C++、アセンブリ言語など他のプログラミング言語で書かれたアプリケーションやライブラリと相互運用できるようになります。
JNI のもっとも重要な利点は、これが基礎となる Java VM の実装に何の制限も課さないということです。そのため、Java VM ベンダーは VM の他の部分に影響を与えずに、JNI のサポートを追加できます。プログラマは、1 つのバージョンのネイティブアプリケーションまたはライブラリを記述すれば、それが JNI をサポートするすべての Java VM 上で動作することを期待できます。
この章では以下について説明します。
Java Native Interface の概要
Java でアプリケーション全体を記述することができる一方で、Java だけではアプリケーションのニーズを満たせない状況もあります。Java でアプリケーション全体を記述できない場合、プログラマは JNI を使用して、「Java ネイティブメソッド」を記述することにより、このような状況に対処できます。
以下に、Java ネイティブメソッドを使用する必要のある場合をいくつか示します。
- 標準 Java クラスライブラリが、アプリケーションに必要なプラットフォーム依存機能をサポートしない場合
- 他の言語で記述されたライブラリをすでに持っており、そのライブラリから JNI を経由して Java コードにアクセスさせる場合
- 一部のタイムクリティカルなコードをアセンブリなどの低レベル言語で実装する必要がある場合
JNI を介して、プログラミングにネイティブメソッドを使用することにより、以下のことが可能になります。
- Java オブジェクト (配列と文字列を含む) の生成、検査、更新
- Java メソッドの呼び出し
- 例外のキャッチおよびスロー
- クラスのロード、およびクラス情報の取得
- 実行時の型チェック
また、「呼び出し API」とともに JNI を使用することにより、任意のネイティブアプリケーションによる Java VM の埋め込みが可能になります。これにより、プログラマは VM ソースコードにリンクしなくても、既存のアプリケーションを Java 対応にできます。
背景
現在、異なるベンダーの VM は異なるネイティブメソッドインタフェースを提供します。これらの異なるインタフェースによって、プログラマは与えられたプラットフォームで複数バージョンのネイティブメソッドライブラリを生成、維持、配布することが必要になります。
既存の代表的なネイティブメソッドインタフェースを以下に紹介します。
- JDK 1.0 ネイティブメソッドインタフェース
- Netscape の Java Runtime Interface
- Microsoft の Raw Native Interface および Java/COM インタフェース
JDK 1.0 ネイティブメソッドインタフェース
JDK 1.0 は、ネイティブメソッドインタフェースを添付して出荷されました。残念ながら、2 つの大きな理由のため、このインタフェースは他の Java VM には適用できません。
第一に、ネイティブコードは Java オブジェクトのフィールドに C 構造体のメンバとしてアクセスします。ただし、Java 言語仕様では、オブジェクトをメモリにどのように配置するかを定義していません。VM がオブジェクトをメモリに異ったやり方で配置する場合、プログラマはネイティブメソッドライブラリを再コンパイルする必要があります。
第ニに、JDK 1.0 のネイティブメソッドインタフェースは古典的なガベージコレクタに依存しています。たとえば、unhand
マクロを無制限に使用すると、ネイティブスタックの古典的な走査が必要になります。
Java Runtime Interface
NetscapeTM は Java 仮想マシンで提供されるサービスの一般的なインタフェースである、Java Runtime Interface (JRI) を提案しました。JRI は移植性を考慮して設計されていますが、基盤となる Java VM の実装の詳細について十分に考慮されていません。JRI はネイティブメソッド、デバッギング、リフレクション、埋め込み (呼び出し) などを含めて、広範囲をサポートしています。
Raw Native Interface および Java/COM インタフェース
Microsoft Java VM は、2 つのネイティブメソッドインタフェースをサポートします。低レベルでは、効率的な Raw Native Interface (RNI) を提供します。JDK のネイティブメソッドインタフェースとのソースレベルの高度の下位互換性を提供しますが、RNI には大きな違いが 1 つあります。厳格なガベージコレクションに依存する代わりに、ネイティブコードは RNI 機能を使用しガベージコレクタと明示的に相互動作しなければなりません。
高レベルでは、Microsoft の Java/COM インタフェースは Java VM に対して言語独立の標準バイナリインタフェースを提供します。Java コードは COM オブジェクトを Java オブジェクトであるかのように使用できます。Java クラスもまた COM クラスとしてシステムの残りに開示することができます。
目的
充分検討された標準インタフェースには、次のような利点があります。
- 各 VM ベンダーはネイティブコードのより大きな本体をサポートできる
- ツールビルダは、異なる種類のネイティブメソッドインタフェースを維持する必要はない
- アプリケーションプログラマは、ネイティブコードの 1 つのバージョンを書くだけでよく、このバージョンは異なる VM 上で動作する
標準のネイティブメソッドインタフェースを確立する最善の方法は、Java VM に関心のあるすべての関係者を取り込むことです。このため、一様なネイティブメソッドインタフェースの設計について Java ライセンス保持者の間で一連の検討を行いました。それにより、標準のネイティブメソッドインタフェースは、次の要件を満たす必要があることが明らかになりました。
- バイナリ互換 - 主要な目標は、与えられたプラットフォーム上のすべての Java VM 実装全体でのネイティブメソッドライブラリのバイナリ互換。プログラマは、1 つのプラットフォームで複数のバージョンのネイティブメソッドライブラリを管理することはできません。
- 効率 - タイムクリティカルコードをサポートするためには、ネイティブメソッドインタフェースはわずかのオーバーヘッドしか課してはならない。VM 非依存 (およびバイナリ互換) を保証する既知の技術のすべては、一定量のオーバーヘッドをもたらします。効率性と VM 非依存の間で、ある程度の妥協をする必要があります。
- 機能性 - インタフェースはネイティブメソッドが有用なタスクを達成できるようにするため、十分に Java 仮想マシンの内部を開示する必要がある。
Java Native Interface のアプローチ
既存のアプローチの 1 つを標準インタフェースとして適用することは望ましいと思われます。 これにより、異なる VM の複数のインタフェースを学ぶ必要があるプログラマにかける負荷は最低限になります。既存の解決策ではこの目標を完全に満足に達成するものは存在しませんでした。
Netscape の JRI は、移植性のあるネイティブメソッドインタフェースとして想定するものにもっとも近いものであり、設計の際の開始点としてこれを使用しました。JRI に慣れ親しんだユーザは、API 命名規則、メソッドとフィールド ID の使用、ローカル参照とグローバル参照の使用などの類似性に気付くでしょう。しかし最善の努力にかかわらず、VM は JRI および JNI の両方をサポートできますが、JNI は JRI とバイナリ互換ではありません。
Microsoft の RNI は、ネイティブメソッドが古典的でないガベージコレクタと協同作業をする際の問題を解決しているため、JDK 1.0 を改善しているといえます。しかし、RNI は VM 非依存ネイティブメソッドインタフェースとしては適当ではありません。JDK のように、RNI ネイティブメソッドは Java オブジェクトに C 構造体としてアクセスします。このため次の 2 つの問題が発生します。
- RNI は、内部 Java オブジェクトの配置をネイティブコードに開示する
- C 構造体として Java オブジェクトに直接アクセスすることによって、高度のガベージコレクションアルゴリズムで必要な「書き込みバリヤ」を効率的に取り込むことができなくなる
バイナリ標準として、COM は異なる VM 間で完全なバイナリ互換を保証します。COM メソッドの起動には間接的な呼び出しだけが必要で、この呼び出しはオーバーヘッドをほとんど伴いません。さらに、COM オブジェクトはバージョン問題の解決という点でダイナミックリンクライブラリに大きな改善をもたらします。
しかし、標準 Java ネイティブメソッドインタフェースとして COM を使用するには、次のいくつかの要因が問題になります。
- 第一に、Java/COM インタフェースは、private フィールドへのアクセスや一般的な例外の発生など、ある種の必要な機能を欠いている
- 第ニに、Java/COM インタフェースは自動的に Java オブジェクトに対して標準の IUnknown および IDispatch COM インタフェースを提供し、ネイティブコードが public メソッドとフィールドをアクセスできるようにする。IDispatch インタフェースはオーバーロードされた Java メソッドを扱わず、メソッド名の照合では大文字と小文字を区別しない。さらに、IDispatch インタフェースを経由して開示されるすべての Java メソッドは、動的型チェックと強制型変換を実行するためにラップされる。これは、IDispatch インタフェースが型付けが弱い言語 (Basic など) を念頭に設計されているため
- 第三に、個別の低レベル関数を扱う代わりに、COM はソフトウェアコンポーネント (独立したアプリケーションを含む) が一緒に動作するように設計されている。すべての Java クラスまたは低レベルネイティブメソッドをソフトウェアコンポーネントとして扱うことは適当でないと考える
- 第四に、COM は UNIX プラットフォーム上でサポートされていないので、すぐには適用できない
Java オブジェクトを COM オブジェクトのようにネイティブコードに開示はしませんが、JNI インタフェース自身は COM とバイナリ互換です。COM が使用するものと同じジャンプテーブル構造体と呼び出し規則を使用します。これは、COM のクロスプラットフォームサポートが使用可能になると、JNI はただちに Java VM の COM インタフェースになれることを意味します。 JNI が、特定の Java VM がサポートする唯一のネイティブメソッドインタフェースであるべきだとは考えません。標準インタフェースは、ネイティブコードライブラリを異なる Java VM にロードしたいプログラマを支援します。あるケースでは、最高の効率を達成するために、プログラマは低レベルな VM 固有インタフェースを使用する必要があります。他のケースでは、プログラマは高レベルインタフェースを使用し、ソフトウェアコンポーネントを構築するかもしれません。実際、Java 環境とコンポーネントソフトウェア技術が円熟してくるにつれて、ネイティブメソッドがその重要性を徐々に失うことを望んでいます。
JNI のプログラミング
ネイティブメソッドプログラマは、JNI のプログラミングを開始する必要があります。JNI のプログラミングは、エンドユーザが実行している可能性のあるベンダーの VM など未知のものから隔離してくれます。JNI 標準に準拠することで、ネイティブライブラリに対して、特定の Java VM で実行できる可能性が高くなります。たとえば、JDK 1.1 は JDK 1.0 で実装された古い型のネイティブメソッドインタフェースのサポートを継続しますが、JDK の将来バージョンでは古い型のネイティブメソッドインタフェースのサポートを中止することは確かです。古い形式のインタフェースに依存するネイティブメソッドは書き直す必要があります。
Java VM を実装する場合には、JNI も実装する必要があります。Java ソフトウェアおよび使用許諾者は、JNI がオブジェクト表現、ガベージコレクション方式などを含め、VM 実装にオーバーヘッドや制限を課さないように最善の努力をしています。我々が見落とした問題を発見した場合には、ご連絡ください。
JDK 1.1.2 での変更点
Java Runtime Environment (JRE) をよりよくサポートするために、JDK 1.1.2 では、呼び出し API の細かい点が拡張されました。これらの変更によって、既存のコードが使用できなくなることがありません。JNI ネイティブメソッドインタフェースは、変更されていません。
-
JDK1_1InitArgs
構造体の reserved0
フィールドの名前が、version
に変更された。JDK1_1InitArgs
構造体は、JNI_CreateJavaVM
への初期化引数を保持する。JNI_GetDefaultJavaVMInitArgs
および JNI_CreateJavaVM
の呼び出し側は、バージョンフィールドを 0x00010001
に設定する必要がある。JNI_GetDefaultJavaVMInitArgs
は、要求されたバージョンがサポートされているかどうかを示す jint
を返すように変更された
-
JDK1_1InitArgs
構造体の reserved1
フィールドの名前は、properties
に変更された。これは、null
で終わる文字列の配列である。各文字列は、システムプロパティを示す次の書式を使う
name=value
この機能は、java コマンド行の -D オプションに相当する
- Java 1.1.1 では、VM 内のユーザスレッドが、唯一の
DestroyJavaVM
を呼び出すスレッドでなければならない。JDK 1.1.2 では、この制約が取り除かれた。複数のユーザスレッドが存在するときに DestroyJavaVM
が呼び出された場合は、VM は、現在のスレッドがユーザスレッドだけになるまで待機し、それから自分自身を破棄しようとする
* この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
目次 | 前の項目 | 次の項目
Java Native Interface 仕様 (1997 年 3 月 15 日に dkramer によって生成された HTML)
Copyright © 1996, 1997 Sun Microsystems, Inc.All rights reserved
コメントや訂正は、jni@java.sun.com までお送りください。