2.13 共有サーバーに関する考慮事項

注意:

パフォーマンス上の理由から、専用サーバーをお薦めします。また、専用サーバーでは、複数のコールにわたって保持されるスレッドおよびソケットに依存するアプリケーションのクラスをサポートしています。たとえば、JMXエージェント接続機能などです。

共有サーバーを使用するセッションでは、複数のコールにわたる制限が存在します。これは、共有サーバーを使用するセッションは、後続のデータベース・コールによる同一プロセスへの接続が保証されていないためで、したがって、複数コールにわたって保持する必要があるセッション固有のメモリーおよびオブジェクトは、SGAに保存されます。つまり、スレッド、オープン・ファイルおよびソケットなどのプロセス固有のリソースは、各コールの終了時にクリーン・アップする必要があるため、次のコールでは使用できません。

この項の内容は次のとおりです。

2.13.1 コール終了時の移行

共有サーバー・モードの場合、Oracle Databaseでは、Javaプログラムの状態を次のコールまで保持するために、コール終了時にstatic変数から参照可能なすべてのオブジェクトがセッション・スペースに移行されます。セッション・スペースはクライアントのセッション内にあり、複数コールにわたって存続するstatic変数およびオブジェクトが格納されます。この移行処理は、各コールの終了時にOracle JVMで自動的に実行されます。

この移行処理にはメモリーが使用され、パフォーマンスにも影響します。したがって、複数のコールにわたって保持するstatic変数やオブジェクトは最小限に抑えるように注意してください。static変数にオブジェクトを不要に格納すると、メモリー・マネージャが移行処理を実行し、セッションごとにリソースを使用することになるため、不要な負担がかかります。static変数を必要最低限に抑えることで、メモリー・マネージャの負担を軽減し、サーバーのパフォーマンスを向上させることができます。

Javaプログラムを同時に実行できるユーザー数を最大にするには、セッションのフットプリントを最小にすることが重要です。特に、拡張性を最大にするには、非アクティブなセッションが占有するメモリー領域を必要最小限にする必要があります。フットプリントを最小化する簡単な方法は、コール終了ごとに大きなデータ構造を解放することです。多くのデータ構造は、別のコールで再度必要になったときに遅延して再作成できます。このような理由から、Oracle JVMには、コール終了時など、セッションが非アクティブになる直前に、指定のJavaメソッドをコールするためのメカニズムがあります。

このメカニズムは、EndOfCallRegistry通知と呼ばれます。これによって、コール終了時にstatic変数を消去し、次のコールの着信時に遅廷初期化テクニックを使用して変数を再初期化できます。これは、複数コール間で保持するためにメモリー・マネージャが必要としている記憶域の容量に懸念がある場合のみ実行してください。Javaで実装する複雑でステートフルなサーバー・アプリケーションの場合のみ、懸念することになります。

コール終了時にデータ構造をNULLにリセットし、その後新しいコールごとにデータ構造を再作成するかどうかの決定は、時間と領域のトレードオフの問題です。データ構造の再作成によって処理時間は多少長くなりますが、次のコールまでデータ構造を保持する必要がないため、メモリー領域をかなり節約できます。また、セッション領域に移行されたオブジェクト(特に大型のオブジェクト)のアクセスには時間がかかり、効率が悪くなります。これは、セッションの表現がコール領域に基づくオブジェクトとは異なることに起因しています。

このような最適化の対象として、次のデータ構造があります。

  • バッファまたはキャッシュ。

  • 静的フィールド(配列など)。これは、一度初期化するとプログラムの実行が完了するまで変化しません。

  • コール間の領域使用効率およびコール中の速度効率の向上が期待できる、動的に作成されるデータ構造。これによってコードが複雑になり、管理が困難になることもあります。したがって、労力に見合う程度の領域が節約される場合のみ、これの実施を検討してください。

2.13.2 コール終了時の最適化に対するOracle固有のサポート

コール終了時に消去するstatic変数は、バッファ、フィールドまたはデータ構造の作成時に登録できます。oracle.aurora.memoryManager.EndOfCallRegistryクラスでは、registerCallback()メソッドによって、Callbackオブジェクトを実装するオブジェクトが取得されます。registerCallback()メソッドには、このオブジェクトがコール終了時まで格納されます。コール終了時に、Oracle JVMは登録されたすべてのCallbackオブジェクトのact()メソッドをコールします。Callbackオブジェクト内のact()メソッドは、ユーザー定義のバッファ、フィールドまたはデータ構造を消去するために実装されます。消去されると、Callbackオブジェクトはレジストリから削除されます。

注意:

コール終了時がセッションの終了時でもある場合は、いずれにしてもセッション・スペースが消去されるため、コールバックは開始されません。

弱い表では、エンドオブコール・コールバックのレジストリが保持されます。Callbackオブジェクトまたは値のいずれかがJavaプログラムから参照できない場合は、オブジェクトと値の両方が表から削除されます。弱い表を使用してコールバックを保持すると、コールバックを登録しても、ガベージ・コレクタによるオブジェクトの削除は回避されません。したがって、必要なコールバックを自分で保持する必要があり、表に保持しておくことはできません。

EndOfCallRegistryの使用方法は、操作対象のオブジェクトがstaticフィールドに保持されているか、インスタンス・フィールドに保持されているかによって異なります。

静的フィールド

EndOfCallRegistryを使用して、クラス全体に対応付けられた状態を消去します。この場合、Callbackオブジェクトはprivate staticフィールドに保持する必要があります。コール間で削除されたキャッシュ内のデータにアクセスする必要があるコードは、キャッシュ・データを遅延して作成または再作成するメソッドをコールする必要があります。

次の例を考えてみます。

import oracle.aurora.memoryManager.Callback;
import oracle.aurora.memoryManager.EndOfCallRegistry;

class Example
{
  static Object cachedField = null;
  private static Callback thunk = null;

  static void clearCachedField()
  {
    // clear out both the cached field, and the thunk so they don't
    // take up session space between calls
    cachedField = null;
    thunk = null;
  }

  private static Object getCachedField()
  {
    if (cachedField == null) 
    {
      // save thunk in static field so it doesn't get reclaimed
      // by garbage collector
      thunk = new Callback () {
        public void act(Object obj)
        {
          Example.clearCachedField();
        }
      };
      
      // register thunk to clear cachedField at end-of-call.
      EndOfCallRegistry.registerCallback(thunk);
      // finally, set cached field
      cachedField = createCachedField();
    }
    return cachedField;
  }

  private static Object createCachedField()
  {
    ...
  }
}

この例では、次の手順が実行されます。

  1. staticフィールドthunkCallbackオブジェクトを作成します。

  2. このCallbackオブジェクトをコール終了時の移行に登録します。

  3. Callback.act()メソッドを実装して、Callbackオブジェクト自体も含めて、すべてのstatic変数を解放します。

  4. キャッシュを遅延して再作成するためのcreateCachedField()メソッドを指定します。

ユーザーがキャッシュを作成すると、CallbackオブジェクトがgetCachedField()メソッドに自動的に登録されます。コール終了時に、Oracle JVMは、登録されたCallback.act()メソッドをコールして、静的メモリーを解放します。

インスタンス・フィールド

インスタンス・フィールドに保持されているデータ構造を消去状態にするには、EndOfCallRegistryを使用します。たとえば、状態があるクラスの各インスタンスに対応付けられている場合、各インスタンスにはそのインスタンスのキャッシュ状態を保持するフィールドがあり、必要に応じてキャッシュ・フィールドが埋められます。状態が確実にキャッシュされるメソッドを使用してキャッシュ・フィールドにアクセスできます。

次に例を示します。

import oracle.aurora.memoryManager.Callback;
import oracle.aurora.memoryManager.EndOfCallRegistry;

class Example2 implements Callback
{
  private Object cachedField = null;

  public voidact (Object obj)
  {
    // clear cached field
    cachedField = null;
    obj = null;
  }

  // our accessor method
  private static Object getCachedField()
  {
    if (cachedField == null)
    {
      // if cachedField is not filled in then you must
      // register self, and fill it in.
      EndOfCallRegistry.registerCallback(self);
      cachedField = createCachedField();
    }
    return cachedField;
  }

  private Object createCachedField()
  {
    ...
  }
}

この例では、次の手順が実行されます。

  1. インスタンスをCallbackオブジェクトとして実装します。

  2. Callback.act()メソッドを実装して、インスタンス・フィールドを解放します。

  3. ユーザーがキャッシュを要求すると、Callbackオブジェクトによって、このオブジェクト自体がコール終了時の移行に登録されます。

  4. キャッシュを遅延して再作成するためのcreateCachedField()メソッドを指定します。

ユーザーがキャッシュを作成すると、CallbackオブジェクトがgetCachedField()メソッドに自動的に登録されます。コール終了時に、Oracle JVMは、登録されたCallback.act()メソッドをコールして、キャッシュを解放します。

この方法を使用すると、Callbackオブジェクトとそのインスタンスが同じオブジェクトになるため、オブジェクトの存続期間とそのインスタンスの存続期間が等しくなります。

2.13.3 EndOfCallRegistry.registerCallback()メソッド

registerCallback()メソッドは、Callbackオブジェクトをレジストリにインストールします。コール終了時に、Oracle JVMは登録されたすべてのCallbackオブジェクトのact()メソッドをコールします。

Callbackオブジェクトは、自動的に、またはObjectインスタンスを使用して登録できます。オブジェクトに格納されたその他の情報をact()に渡す必要がある場合は、このオブジェクトを、Objectのインスタンスであるvalueパラメータに登録できます。

registerCallback()メソッドの有効なシグネチャは、次のとおりです。

public static void registerCallback(Callback thunk, Object value);

public static void registerCallback(Callback thunk);

次の表に、registerCallbackのパラメータとその説明を示します。

パラメータ 説明

thunk

コール終了時の移行でコールされるCallbackオブジェクトを示します。

value

オブジェクトに格納されたその他の情報をact()に渡す必要がある場合は、このオブジェクトをvalueパラメータに登録できます。場合によっては、コールバックに必要な状態の保持にvalueパラメータが必要になります。ただし、大半のユーザーは、このパラメータの値を指定する必要はありません。

2.13.4 EndOfCallRegistry.runCallbacks()メソッド

runCallbacks()メソッドのシグネチャは、次のとおりです。

static void runCallbacks()

JVMはコール終了時にこのメソッドをコールし、registerCallback()を使用して登録されたすべてのCallbackオブジェクトごとにact()をコールします。コール終了時にこれがコールされた後で、オブジェクトが移行され、最終ステップが行われます。

注意:

このメソッドは、作成したコード内ではコールしないでください。

2.13.5 Callbackインタフェース

インタフェースの宣言は、次のとおりです。

Interface oracle.aurora.memoryManager.Callback

EndOfCallRegistry.registerCallback()を使用して登録するオブジェクトは、Callbackインタフェースを実装する必要があります。このインタフェースは、アプリケーションで、コール終了時に通知が必要な場合に役立ちます。

2.13.6 Callback.act()メソッド

act()メソッドのシグネチャは、次のとおりです。

public void act(Object value)

コールの終了時に実行する必要がある任意のアクティビティを実装できます。通常、このメソッドにはセッション・スペースに保存されるメモリーを消去するプロシージャが含まれます。

2.13.7 複数のコールに影響を与えるオペレーティング・システム・リソース

次の表に示すとおり、共有サーバー・モードでは、データベース・コールの終了時にOracle JVMが、オープン状態のオペレーティング・システム・リソースをすべてクローズします。

リソース 存続期間

ファイル

データベースのコールが終了すると、オープン状態のすべてのファイルがクローズされます。

スレッド

コールが終了すると、すべてのスレッドが終了します。

ソケット

  • クライアント・ソケットは複数コールにわたって存続できます。

  • サーバー・ソケットは、コールが終了すると終了します。

オペレーティング・システム・リソースに依存するオブジェクト

Javaオブジェクトの有効期間は、そのオブジェクトの使用可能期間に関係なく、セッションが存続している期間になります。たとえば、staticクラス変数あるいはこのクラスを直接または間接的に参照するクラス変数にJavaオブジェクトを格納すると、セッション終了まで有効になります。使用可能期間の終了後にJavaオブジェクトを使用しようとすると、Oracle Databaseから例外がスローされます。これは次のような例に当てはまります。

  • 前のコールの終了時にクローズしたjava.io.FileInputStreamから読み取ろうとすると、java.io.IOExceptionが発生します。

  • 以前のコールで実行したThreadオブジェクトが後続のコールでもアクセス可能な場合は、java.lang.Thread.isAlive()falseになります。

1つのコールの終了時には、そのコールに対してローカルなリソースをクローズしてください。ただし、オペレーティング・システム・リソースを保持するstaticオブジェクトについては、これらのリソースがコール終了後に受ける影響を考慮する必要があります。

ファイル

共有サーバー・モードでは、コールが終了すると、Oracle JVMはオープン状態のオペレーティング・システム構成メンバーを自動的にクローズします。これは、Javaオブジェクト内のオペレーティング・システム・リソースすべてに影響を与えます。static変数の中でファイルをオープンした場合、そのファイル・ハンドルはコールの終了時にクローズされます。したがって、複数のコールにまたがってFileオブジェクトを保持すると、次にファイル・ハンドルを使用するときに例外がスローされます。

次の例のConcatクラスは、複数のファイルを1つのファイルoutFileに書き込むことができます。最初のコールでoutFileが作成されます。最初の入力ファイルがオープンされ、読み取られてoutFileに記録され、コールが終了します。outFilestatic変数として定義されているため、次のコールが起動される間にセッション・スペースに移動します。ただし、ファイル・ハンドルは、コールが終了するとクローズします。次回addFile()をコールすると、例外が発生します。

例2-5 オペレーティング・システム・リソースの調整

public class Concat
{
  static File outFile = new File("outme.txt");
  FileWriter out = new FileWriter(outFile);

  public static void addFile(String[] newFile)
  {
    File inFile = new File(newFile);
    FileReader in = new FileReader(inFile);
    int i;

    while ((i = in.read()) != -1)
      out.write(i);
    in.close();
  }
}

これには対処法があります。コールが終了するたびにファイルやバッファなどをクローズし、次のコールの開始時にリソースを再度オープンして、ハンドルを有効な状態に維持します。もう1つの方法は、オペレーティング・システム・リソースではなくデータベースを使用することです。たとえば、ファイルではなくデータベース表を使用します。あるいは、複数のコールにわたって存続するstaticオブジェクトにオペレーティング・システム・リソースを格納しないようにします。かわりに、そのコールに対するローカルなオブジェクトの中でのみ、オペレーティング・システム・リソースを使用します。

次の例では、オペレーティング・システム・リソースのセキュリティ上の問題なしに連結を実行できる方法を示しています。addFile()メソッドは、各コールでoutme.txtファイルをオープンし、そのファイルに書き込まれた内容がすべて最後に追加されていることを確認します。このファイルは、各コールの終了時にクローズします。この場合、次の2つの状態が発生します。

  • Fileオブジェクトはコール外では存続しなくなります。

  • オペレーティング・システム・リソースであるoutme.txtファイルはコールごとに再度オープンされます。Fileオブジェクトをstatic変数に指定した場合は、各コール内でoutme.txtをクローズすることで、オペレーティング・システム・リソースを調整する必要はなくなります。

例2-6 オペレーティング・システム・リソースの正しい管理

public class Concat
{

  public static void addFile(String[] newFile)
  {
    /*open the output file each call; make sure the input*/
    /*file is written out to the end by making it "append=true"*/
    FileWriter out = new FileWriter("outme.txt", TRUE);
    File inFile = new File(newFile);
    FileReader in = new FileReader(inFile);
    int i;

    while ((i = in.read()) != -1)
      out.write(i);
    in.close();
    
    /*close the output file between calls*/
    out.close();
  }
}

ソケット

ソケットは、クライアントとサーバー間の接続をセットアップするために使用されます。データベース接続ごとに、接続の片側でソケットが使用されます。接続のセットアップはアプリケーションでは行われません。接続は、基礎となるプロトコル(Oracle NetのTTCまたはIIOP)で実行されます。

関連項目:

接続の構成方法については、「Oracle JVMの構成」を参照してください。

データベースに格納された1つのクラス内から指定のURLに接続するなど、別の接続をセットアップすることもできます。このセットアップでは、次のコンストラクタを使用して接続のクライアント側とサーバー側に対応するようにソケットをインスタンス化します。

  • java.net.Socket()コンストラクタで、クライアント・ソケットを作成します。

  • java.net.ServerSocket()コンストラクタで、サーバー・ソケットを作成します。

ソケットは接続の両側にあります。着信コールをリスニングするサーバー側の接続に対応するのはServerSocketインスタンスです。要求を送るクライアント側の接続に対応するのはSocketインスタンスです。JVMに定義されたソケットは、共有サーバー内のServerSocketインスタンスは複数のコールにまたがって存続できないという制限の範囲内で使用できます。

次の表に、ソケットの種類とその説明を示します。

ソケットの種類 説明

Socket

接続のクライアント側は送信側になるため、Socketインスタンスは共有サーバー内の複数のコールにわたって対応できます。

ServerSocket

接続のサーバー側のリスナーです。ServerSocketインスタンスは、共有サーバー内の1つのコールが終了するとクローズします。共有サーバーはコールの終了時に別のクライアントに移動します。作成元以外のコールでServerSocketインスタンスを使用すると、ソケットがクローズしていることを示すI/O例外が発生します。

スレッド

共有サーバー・モードでは、戻りまたは捕捉されない例外が原因でコールが終了すると、Oracle JVMは、すべてのデーモン・スレッドでThreadDeathExceptionをスローします。このThreadDeathExceptionは、原則的にスレッドの実行を停止します。複数のコールにわたって存続するスレッドに依存しているコードは、共有サーバー・モードでは予測どおりには動作しません。たとえば、データベース・コールの終了時にはすべてのスレッドが停止するため、スレッドの初期化を追跡するstatic変数の値は、後続のコールでは正確さに欠ける可能性があります。

具体的な例として、標準のRMIサーバーは共有サーバー・モードで機能します。ただし、1つのコールのコンテキスト内のみで有効です。これは、共有サーバー・モードでRMIサーバーがデーモン・スレッドをフォーク処理し、コール終了時に停止するためです。つまり、すべての非デーモン・スレッドが戻ったときにデーモン・スレッドが停止します。後続のコールでRMIサーバー・セッションを再び開始しても、これらのデーモン・スレッドは再開されないため、RMIサーバーは適切に機能しません。