第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
Netscapeは、Java仮想マシンで提供されるサービスの一般的なインタフェースであるJava Runtime Interface (JRI)を提案しました。 JRIは移植性を考慮して設計されましたが、基盤となるJava VMの実装の詳細について十分に考慮されていません。 JRIはネイティブ・メソッド、デバッグ、リフレクション、埋め込み(呼び出し)など、広範囲にサポートしていました。
Raw Native InterfaceおよびJava/COMインタフェース
Microsoft Java VMは、2つのネイティブ・メソッド・インタフェースをサポートします。 低レベルでは、効率的なRaw Native Interface (RNI)を提供します。 RNIは、JDKのネイティブ・メソッド・インタフェースとのソース・レベルの高度な下位互換性を提供しますが、大きな違いが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オブジェクトの配置をネイティブ・コードに開示していた。
- JavaオブジェクトをC構造体に直接アクセスすると、高度なガベージ・コレクション・アルゴリズムに必要な"障壁を書き、"を効率的に組み込むことが不可能になります。
バイナリ標準として、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で実行できる可能性が高くなります。
Java VMを実装する場合には、JNIも実装する必要があります。 JNIは長年にわたり、オブジェクト表現やガベージ・コレクション・スキームなど、VM実装に対するオーバーヘッドや制限を課さないように努力しています。 当社が見落とした問題を発見した場合は、ご連絡下さい。