1.2 Oracle DatabaseにおけるJavaの使用について

Javaは豊富なセキュリティ機能を備えた安全な言語であるため、データベースでJavaアプリケーションを作成およびロードできます。Javaは、Javaコードが格納されているオペレーティング・システムの改ざんを防止するように開発されています。Cなど、一部の言語では、データベースにセキュリティ上の問題が発生する可能性があります。これに対してJavaは、その設計により、データベース内の使用にも堅牢な言語です。

Java言語は開発者にとって多数の利点がありますが、Javaサーバー・アプリケーションをサポートするJVMをスケーラブルな方法で実装するのは容易ではありません。この項では、次の問題について説明します。

1.2.1 JavaとRDBMS: 堅牢な組合せ

Oracle Databaseでは、Javaアプリケーションに対して、複合問合せの実行や同一データの異なるビューでの表示をサポートする動的なデータ処理エンジンが提供されます。クライアントからの要求はすべてデータ問合せとして構成されて即時処理され、問合せ結果がすぐに生成されます。

JavaとOracle Databaseを組み合せることによって、ビジネス・ニーズの変化に応じて簡単に更新できるコンポーネントベースでネットワーク中心のアプリケーションを作成できます。また、アプリケーションおよびデータ・ストアは、デスクトップからインテリジェント・ネットワーク上およびネットワーク中心のサーバー上に移動できます。さらに重要なのは、これらのアプリケーションおよびデータ・ストアに任意のクライアント・デバイスからアクセスできることです。

図1-5に、従来の2層クライアント/サーバー構成を示します。この構成では、クライアントは、PL/SQLストアド・プロシージャのコールと同様の方法でJavaストアド・プロシージャをコールします。また、この図では、Oracle Net Services Connection Managerが、多数のネットワーク接続を単一のデータベース接続に結合する方法も示されています。このようにして、Oracle Databaseでは多数の同時ユーザーがサポートされます。

図1-5 2層クライアント/サーバー構成

図1-5の説明が続きます
「図1-5 2層のクライアント/サーバー構成」の説明

1.2.2 マルチスレッドについて

マルチスレッドは、Java言語の主要なスケーラビリティ機能の1つです。Java言語とクラス・ライブラリによって、他の多くの言語に比べてマルチスレッド・アプリケーションをJavaで簡単に作成できますが、依然としてどのような言語でも、信頼性が高くスケーラブルなマルチスレッド・コードを記述するのは困難な作業です。

Oracle Databaseサーバーは、多数のユーザーの作業を効率的にスケジュールします。Oracle JVMはOracle Databaseのセッション・アーキテクチャを利用して、多数のユーザーのJavaアプリケーションを同時に実行します。Oracle DatabaseではJLSとJCKに必要なJava言語レベルのスレッドがサポートされていますが、データベースの有効範囲内でスレッドを使用するとスケーラビリティは向上しません。データベースの埋込みのスケーラビリティを使用すると、マルチスレッドJavaサーバーを記述する必要がなくなります。

ユーザーのスケジューリングには、シングル・スレッドJavaアプリケーションを作成してOracle Databaseの機能を使用してください。データベースが各アプリケーション間の処理をスケジューリングするため、スレッドを管理しなくてもスケーラビリティが実現します。また、マルチスレッドJavaアプリケーションの作成も可能ですが、複数のJavaスレッドを使用してもサーバーのパフォーマンスは向上しません。

マルチスレッドで生じる問題の1つは、スレッドと自動記憶域管理またはガベージ・コレクションとの間の相互作用です。汎用JVMで実行されるガベージ・コレクタでは、どのJava言語スレッドが実行されているか、または基盤となるオペレーティング・システムでどのようにスケジュールされているかは認識されません。非Oracle DatabaseモデルとOracle JVMモデルの相違点は、次のとおりです。

  • 非Oracle Databaseモデル

    単一のユーザーが単一のJavaスレッドにマップされ、単一のガベージ・コレクタによって全ユーザーからのガベージがすべて管理されます。通常、存続期間やサイズの異なるオブジェクトの割当てと回収は、異なる技法で処理されます。アプリケーションが過度にマルチスレッド化されると、少なくともネイティブ・スレッド用のオペレーティング・システム・サポートに依存するようになるため、信頼性が低下したり、スケーラビリティが限定される可能性があります。この種の実装の場合、高水準のスケーラビリティは十分に実証されていません。

  • Oracle JVMモデル

    多数のユーザーがサーバーに接続して同じJavaコードを実行する場合も、各ユーザーは各自のJVMで独自のJavaコードを実行しているように感じます。Oracle JVMの役割は、オペレーティング・システムのプロセスとスレッド、およびOracle Databaseのスケーラブルなアプローチを利用することです。このアプローチによって、一度に複数のユーザーからガベージが収集されることがなくなるため、Oracle JVMのガベージ・コレクタの信頼性と効率が向上します。

1.2.3 メモリー領域管理

ガベージ・コレクションはJavaの自動記憶域管理機能の主要な機能であり、Java開発者はメモリーの割当てと解放を明示的に行う必要がなくなります。そのため、CやC++のプログラムで一般に検出されるメモリー・リークの大きな原因がなくなります。ただし、ガベージ・コレクションはプログラムの実行スピードとフットプリントのオーバーヘッドに影響を与えます。

ガベージ・コレクションは、高スケーラブルで高速なJavaプラットフォームを提供しようとするJVM開発者に対する1つの課題です。Oracle JVMは、このような課題に次の方法で対応します。

  • Oracle JVMでは、複数のユーザーを効率的に管理できるOracle Databaseのスケジューリング機能が使用されます。

  • ガベージ・コレクションは単一セッション内の単一ユーザーを対象とするため、複数ユーザーに対しても一貫して実行されます。Oracle JVMの場合、ユーザー数が増加してもメモリー・マネージャの作業の負荷や複雑さは変わらないため、その点において有利です。メモリー・マネージャは単一セッション内でオブジェクトの割当てと回収を実行し、通常はこれが単一ユーザーのアクティビティに変換されます。

  • Oracle JVMでは、使用するメモリーのタイプに応じて異なるガベージ・コレクションの技法が使用されます。これらの技法によって、効率が向上し、オーバーヘッドが低減します。

2種類のメモリー領域は、コール領域とセッション領域です。

メモリー領域 説明

コール領域

高速でコストの低いメモリーです。主にコール期間中に存在します。コール・メモリー領域は新規セグメントと旧セグメントに分割されます。新規オブジェクトはすべて新規メモリー内に作成されます。数回のスキャベンジ後も存在し続けているオブジェクトは、旧メモリーに移動されます。

セッション領域

コストの高いパフォーマンス重視のメモリーです。主にセッション期間中に存在します。コール存続期間より長い期間存在するすべてのstaticフィールドとオブジェクトは、この領域に存在します。

図1-6は、ガベージ・コレクタによって実行される様々なアクションを示しています。

図1-6 ガベージ・コレクション

図1-6の説明が続きます
「図1-6 ガベージ・コレクション」の説明

Oracle JVM内のガベージ・コレクション・アルゴリズムは、次のルールを遵守します。

  1. 新規オブジェクトは新規コール領域内に作成されます。

  2. スキャベンジは既定の間隔で行われます。プログラマによっては、オブジェクトを短期間のみ頻繁に作成する場合があります。このようなタイプのオブジェクトは、新規コール領域内に作成され、すぐにガベージ・コレクションされます。これはスキャベンジと呼ばれます。

  3. スキャベンジを数回繰り返した後も存在し続けているオブジェクトは、しばらくの間存在する可能性のあるオブジェクトとみなされます。これらのオブジェクトは、新規コール領域から旧コール領域に移動されます。移動中にも圧縮が行われます。旧コール領域はスキャベンジ(ガベージ・コレクション)される回数が少ないため、パフォーマンスが向上します。

  4. コール終了時に、コール後も存在するオブジェクトはセッション領域に移動されます。

図1-6は、前述の説明の手順を示しています。このアプローチでは、オブジェクトの種類や存続期間にあわせて、効率的な割当と回収の仕組みが適用されます。たとえば、新規オブジェクトは、迅速な割当とアクセスを目的として設計された、高速でコストの低いコール・メモリーに割り当てられます。Java staticフィールドで保持されるオブジェクトは、より貴重でコストの高いセッション領域に移行されます。

1.2.4 フットプリント

Javaプログラムの実行によるフットプリントは、次の様々な要因の影響を受けます。

  • プログラムのサイズ

    プログラムのサイズは、クラス数、メソッド数およびそれらのコードの量によって決まります。

  • プログラムの複雑さ

    プログラムの複雑さは、プログラム自体ではなく、プログラムの実行時にOracle JVMで使用されるコア・クラス・ライブラリの数によって決まります。

  • JVMが使用する領域の量

    JVMが使用する領域の量は、JVMが割り当てるオブジェクトの数とサイズ、および複数コール間で保持する必要のあるオブジェクトの数によって決まります。

  • ガベージ・コレクタとメモリー・マネージャが、プログラムの実行の要求を処理する能力

    ほとんどの場合、この能力は決定要因ではありません。オブジェクトの割当て速度および他のオブジェクトによって保持される方法が、この要因の重要度に影響を与えます。

スケーラビリティの観点から、複数のクライアントを同時にサポートするには、ユーザー当たりのセッション・フットプリントを最小限に抑えることが重要です。Oracle JVMでは、Javaバイトコードなど、ユーザー用のすべての読取り専用データを共有メモリーに格納することで、ユーザー当たりのセッション・フットプリントを最小限に抑えます。ユーザー・セッションのフットプリントが大きくならないように、コールとセッションのメモリーに対して、適切なガベージ・コレクション・アルゴリズムが適用されます。Oracle JVMでは、ユーザーのセッション・メモリーのメンテナンスに、次の種類のガベージ・コレクション・アルゴリズムが使用されます。

  • ジェネレーション・スキャベンジ: 存続期間の短いオブジェクトの場合

  • マーク/レイジー・スイープ・コレクション: 単一コールの間に存在するオブジェクトの場合

  • コレクタのコピー: 存続期間の長いオブジェクト、つまり、セッション内の複数コールにわたって存在するオブジェクトの場合

1.2.5 Oracle JVMのパフォーマンス

Oracle JVMのパフォーマンスは、標準のHotSpotと同様に革新的なJust-In-Timeコンパイラを組み込むことによって向上します。プラットフォームに依存しないJavaバイトコードはJVMの上部で実行され、そのJVMは特定のハードウェア・プラットフォームと対話します。ソフトウェア内でレベルを追加すると、パフォーマンスが低下します。Javaでは、バイトコードを解析するために仲介部分を通過する必要があるため、Javaアプリケーションには、C言語のようなプラットフォームに依存する言語を使用して開発されたアプリケーションと比較して、ある程度の非効率性が伴います。この問題に対処するために、複数のJVMサプライヤがネイティブ・コンパイラを作成しています。ネイティブ・コンパイラによって、Javaバイトコードはプラットフォームに依存するネイティブ・コードに変換され、その結果、インタプリタのステップが不要になり、パフォーマンスが向上します。

次の表に、ネイティブ・コンパイルの2つの方法を示します。

コンパイラ 説明

Just-In-Time (JIT)コンパイル

JITコンパイラは、実行時にJavaバイトコードをプラットフォーム固有(ネイティブ)のマシン・コードに迅速にコンパイルします。これらのコンパイラでは、プラットフォームで実行される実行可能ファイルは生成されません。かわりに、変換後に直接実行されるプラットフォームに依存するコードがJavaバイトコードから作成されます。JITコンパイラは、頻繁に実行され、Cなどの他言語で開発されたコードとほぼ同じ速度で実行されるJavaコードに対して使用してください。

Ahead-of-Timeコンパイル

このコンパイルでは、実行前にJavaバイトコードがプラットフォームに依存しないCコードに変換されます。次に、標準的なCコンパイラで、Cコードがターゲット・プラットフォーム用の実行可能ファイルにコンパイルされます。この方法は、あまり変更されないJavaアプリケーションに適しています。この方法は、最近のCコンパイラで採用されている成熟した効率的なプラットフォーム固有のコンパイル技術を利用しています。

Oracle Databaseは、Just-In-Time (JIT)コンパイルを使用して、JDBCコードなどのコアなJavaクラス・ライブラリをネイティブにコンパイル済の形式で配布しています。JITコンパイラは、プラグインのサポートがなくても有効で、これは、Oracleがサポートしているすべてのプラットフォームで適用できます。

次の図に示すように、ネイティブにコンパイルされたコードは、解析されるコードに比較して最高で10倍速く実行されます。この結果、プログラムで使用するネイティブ・コードが多いほど、実行速度が速くなります。

図1-7 インタプリタとAccelerator

図1-7の説明が続きます。
「図1-7 インタプリタとAccelerator」の説明

1.2.6 動的クラス・ロード

Javaのもう1つの強力な機能は、動的クラス・ロードです。クラス・ローダーは、クラスをディスクからロードし、解析に必要なJVM固有のメモリー構造に格納します。また、クラスをCLASSPATH内で検索し、プログラムの実行中に使用される場合にのみロードします。この方法はアプレットに適していますが、サーバー環境では次の問題があります。

問題 説明 解決策

予測可能性

クラス・ロード操作は、プログラムの初回実行時に重大なペナルティを伴います。単純なプログラムの場合でも、Oracle JVMによって、そのニーズをサポートするための多数のコア・クラスがロードされる可能性があります。ロードされるクラスの数は、プログラマが簡単に予測または判断することはできません。

Oracle JVMは、他のJVMと同様に、クラスを動的にロードします。1回のクラス・ロードの速度は同じです。ただし、クラスは共有メモリーにロードされるため、そのクラスの他のユーザーはクラスを再ロードする必要はなく、同じ事前ロード・クラスを使用します。

信頼性

動的クラス・ロードの利点は、プログラムの更新がサポートされることです。たとえば、サーバー上でクラスを更新すると、プログラムをダウンロードして動的にロードするクライアントでは、次回そのプログラムを使用するときに更新されたことがわかります。サーバー・プログラムでは、信頼性が重視されます。開発者は、各クライアントが特定のプログラム構成を実行していることを認識する必要があります。意図していない一部のクラスがクライアントによって不注意にロードされないようにしてください。

Oracle Databaseでは、アップロードおよび解決操作が、実行時のクラス・ロード操作と分離されます。開発したJavaコードをサーバーにアップロードするには、loadjavaツールを使用します。CLASSPATHを使用するかわりに、インストール時にリゾルバを指定します。リゾルバはCLASSPATHに類似していますが、クラスが存在するスキーマを指定できます。このように解決操作をクラス・ロードから分離することで、ユーザーがどのプログラムを実行しているかを常に把握できます。