3 RMIシステムの概要


3.1 スタブとスケルトン

RMIでは、リモート・オブジェクトであるスタブおよびスケルトンとの通信に、RPCシステムで採用されている標準のメカニズムが使用されます。 リモート・オブジェクトのスタブは、リモート・オブジェクトに対するクライアントのローカルでの代理、つまりプロキシの役割を果たします。 呼出し側は、リモート・オブジェクトに対するメソッド呼出しを行うローカル・スタブ上のメソッドを呼び出します。 RMIでは、リモート・オブジェクトのスタブは、リモート・オブジェクトが実装しているリモート・インタフェースのセットと同じセットを実装します。

スタブのメソッドは、呼び出されると、次の動作を実行します。

スタブが、パラメータの直列化およびネットワーク・レベルの通信を隠すことにより、呼出し側に対してシンプルな呼出しメカニズムが提供されます。

リモートJVMでは、各リモート・オブジェクトに対応するスケルトンがあります。Java 2プラットフォームのみの環境では、スケルトンは必要ありません。 スケルトンによって、呼出しが実際のリモート・オブジェクトの実装にディスパッチされます。 スケルトンは、メソッド呼出しを受け取ると、次の動作を実行します。

Java 2 SDK, Standard Edition, v1.2では、スタブ・プロトコルが追加され、Java 2プラットフォームだけの環境ではスケルトンが必要なくなりました。 JDK 1.1でスケルトンによって行われた作業は、ジェネリック・コードを使って実行されます。 スタブおよびスケルトンは、rmicコンパイラによって生成されます。

3.2 リモート・メソッド呼出しにおけるスレッドの使用法

RMIランタイムがリモート・オブジェクトの実装へディスパッチしたメソッドは別なスレッドで実行される場合と、そうでない場合があります。 RMIランタイムは、リモート・オブジェクト呼び出しとスレッドとのマッピングに対しては何の保証も行いません。 同一のリモート・オブジェクトに対するリモート・メソッド呼出しは並列に実行されることがあるため、リモート・オブジェクトの実装は、その実装がスレッドに対して安全であることを確認する必要があります。

3.3 リモート・オブジェクトのガベージ・コレクション

ローカル・システムにおけるのと同様に、分散システムにおいても、どのクライアントからも参照されなくなったリモート・オブジェクトは自動的に削除されるのが望ましいことです。 これによりプログラマは、時間の制約の中でリモート・オブジェクトのクライアントの追跡を終了させるという仕事から解放されます。 RMIは参照カウント方式の、Modula-3のNetwork Objectsに似たガベージ・コレクション・アルゴリズムを採用しています。 (Birrell、Nelson、Owickiによる次の文献を参照。『Network Objects』Digital Equipment Corporation Systems Research Center Technical Report 115, 1994。)

参照カウント方式ガベージ・コレクションを実行するために、RMIのランタイムは各Java仮想マシン内のライブ参照を常に監視しています。 ライブ参照がJava仮想マシンに入ってくると参照カウントは増加します。 あるオブジェクトへの最初の参照は「参照が起こった」ことを示すメッセージをそのオブジェクトのサーバーへ送ります。 ライブ参照がローカルな仮想マシン内で参照されていないことがわかると、カウントは減少します。 最後の参照が廃棄されると、「参照されていない」ことを示すメッセージがサーバーに送られます。 このプロトコルには微妙な点が数多くありますが、オブジェクトの早過ぎる収集が起こらないことを保証するためには、それらは大部分、参照が起こったこと、参照されていないことを示すメッセージの順序付けの管理に関連しています。

リモート・オブジェクトがどのクライアントからも参照されていない状態になると、RMIランタイムは、そのオブジェクトを弱参照(weak reference)によって参照します。 弱参照になるとJava仮想マシンのガベージ・コレクタは、もしそのオブジェクトへのローカル参照が他に存在しなければ、オブジェクトは廃棄します。 分散ガベージ・コレクション・アルゴリズムは、オブジェクトへのノーマルな参照と弱参照を保持しながら、ローカルJava仮想マシンのガベージ・コレクタと相互作用を続けます。

リモート・オブジェクトへのローカルな参照が存在している間は、オブジェクトがガベージ・コレクトされることはなく、オブジェクトはリモート呼出しに渡されたり、クライアントに返されたりします。 リモート・オブジェクトを渡すと、それが渡された仮想マシンの識別子が参照セットに追加書込みされます。 非参照の通知を必要とするリモート・オブジェクトは、java.rmi.server.Unreferencedインタフェースを実装する必要があります。 これらの参照が存在しなくなると、unreferencedメソッドが呼び出されます。unreferencedは、参照セットが空であると呼び出されるため、複数回呼び出されることがあります。 リモート・オブジェクトが回収されるのは、リモートとローカルを含めて参照がまったくなくなった場合のみです。

クライアントとリモート・サーバーの間にネットワーク・パーティションが存在すると、リモート・オブジェクトのコレクションが早すぎる時期に行われることがあります。これは、トランスポートによってクライアントがクラッシュしたと判断される可能性があるためです。 時期尚早な回収の可能性があるため、リモート参照は参照に関する完全な整合性を保証するものではありません。言い換えれば、リモート参照が実際に存在するオブジェクトを参照していない可能性が常に存在するということです。 このような参照を使おうとするとRemoteExceptionが発生しますが、その取扱いはアプリケーションが行う必要があります。

3.4 動的クラス・ロード

RMIでは、直列化可能な任意のオブジェクトを、RMI呼出しに渡すパラメータ、戻り値、および例外とすることができます。 また、オブジェクトの直列化メカニズムを使って、仮想マシン間でデータを転送したり、受信側でクラス定義ファイルをロードできるように、呼出しストリームに適切な場所情報を注釈として付ける処理を行います。

リモート・メソッド呼出しのためのパラメータおよび戻り値をライブ・オブジェクトにするために、受信側のJVMで非整列化するときは、ストリーム内に存在するオブジェクトのすべての型のクラス定義が必要になります。 非整列化処理では、まず、ローカル・クラスのロード・コンテキスト(現在のスレッドのコンテキスト・クラス・ローダー)内の名前によってクラスの解決処理が試みられます。 また、RMIでは、転送するエンド・ポイントで指定されたネットワークの場所から、リモート・メソッド呼出しのパラメータや戻り値として渡されるオブジェクトの実際の型のクラス定義を動的にロードする機能も提供されています。 これには、特定のリモート・オブジェクトの実装クラスに対応するリモート・スタブ・クラス(リモート参照の格納に使用)の動的なダウンロードも含まれます。また、宣言されたパラメータの型のサブクラスなど、RMI呼出しで値によって渡される他の型のクラスのうち、非整列化する側のクラスのロード・コンテキストでまだ使用可能になっていないクラスを動的にダウンロードすることもできます。

クラスの動的なロードをサポートするために、RMIランタイムでは、RMIパラメータと戻り値の整列化および非整列化に使う整列化ストリームに、java.io.ObjectOutputStreamおよびjava.io.ObjectInputStreamの特別なサブクラスを使います。 これらの各サブクラスによってObjectOutputStreamannotateClassメソッド、およびObjectInputStreamresolveClassメソッドがオーバーライドされ、ストリーム内のクラス記述子に対応するクラス定義を含むクラス・ファイルの場所についての情報が伝えられます。

RMI整列化ストリームに書き込まれたクラス記述子ごとに、そのクラス・オブジェクトのjava.rmi.server.RMIClassLoader.getClassAnnotationの呼出し結果がannotateClassメソッドによってストリームに追加されます。この結果は、nullか、Stringオブジェクトです。Stringオブジェクトは、リモート・エンドポイントがそのクラスのクラス定義ファイルをダウンロードする、元のコード・ベースURLパス(スペースで区切られたURLの一覧)を表します。

RMI整列化ストリームから読み取られたクラス記述子ごとに、resolveClassメソッドによって、ストリームからオブジェクトが1つ読み取られます。 オブジェクトがStringの場合、およびjava.rmi.server.useCodebaseOnlyプロパティの値がtrueでない場合、resolveClassRMIClassLoader.loadClass呼出しの結果を返します。この呼出しの最初のパラメータは注釈の付けられたStringオブジェクトであり、2番目のパラメータはクラス記述子内の目的のクラス名です。 これ以外の場合、resolveClassは、クラス名を唯一のパラメータとするRMIClassLoader.loadClass呼出しの結果を返します。

RMIでのクラスのロードの詳細については、"RMIClassLoaderクラス"を参照してください。

3.5 プロキシ経由でファイアウォールを通過するRMI

プロキシ経由のファイアウォール経由のRMI呼び出しの実装は、JDK 9では削除されています。