目次 | 前へ | 次へ

はじめに


第 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 オブジェクトの配置をネイティブコードに開示していた。
  • 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 で実行できる可能性が高くなります。

Java VM を実装する場合には、JNI も実装する必要があります。JNI は長年にわたり、オブジェクト表現やガベージコレクションスキームなど、VM 実装に対するオーバーヘッドや制限を課さないように努力しています。当社が見落とした問題を発見した場合は、ご連絡下さい。

変更点

Java SE 6.0 では、非推奨の構造体 JDK1_1InitArgs および JDK1_1AttachArgs を削除し、それに代わって JavaVMInitArgs および JavaVMAttachArgs を使用します。

 


目次 | 前へ | 次へ

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