Sun ONE ロゴ     前へ      目次      索引      次へ     
Sun ONE Application Server 7 パフォーマンスチューニングガイド



Java 実行システムのチューニング

Solaris 環境は、デフォルトで 2 レベルのスレッドモデルをサポートしています (Solaris 8 まで)。アプリケーションレベルの Java スレッドは、ユーザーレベルの Solaris スレッドにマッピングされ、制限のあるライトウェイトプロセス (LWP) プール上で多重化されます。システムのプロセッサと同じ数の LWP があるだけで、カーネルリソースの保存とシステム効率の向上が可能になることがよくあります。これは、ユーザレベルのスレッドが何百もある場合に有効です。幸いにも (あるいは不幸にも)、複数のスレッドモデル、複数のモデル内同期メソッドから選択することができますが、これは VM ごとに異なります。さらに、スレッドライブラリが Solaris 8 から 9 に移行したことで、多くの選択肢が消滅したことも問題を複雑にしています。1.4 VM には 2 レベルのモデルがありますが、デフォルトでは VM は LWP ベースの同期を使うので、効率的な 1 対 1 のスレッド / LWP モデルを利用できます。

この章では、次のトピックについて説明します。

代替スレッドの使用

Solaris 8 では、LD_LIBRARY_PATH/usr/lib の前に /usr/lib/lwp を挿入することで、/usr/lib/lwp/ に保存されている代替スレッド libthread.so をロードできます。一部のアプリケーションでは、特に、使っているスレッドが少ない場合は、スループットとシステム利用効率が向上します。

デフォルトでは、Sun ONE Application Server は /usr/lib/lwp を使用します。デフォルトの設定を変更して LWP を使わないようにするには、startserv スクリプトの LD_LIBRARY_PATH から /usr/lib/lwp を削除します。ただし、特に必要のない場合は変更しないでください。

多数のスレッドを使うアプリケーションでは、/usr/lib/libthread.so ライブラリが適しています。これによって 1.4 のデフォルトである LWP ベースの同期が有効になるだけでなく、TLABS (スレッドローカル割り当てバッファ) が無効化され、ヒープがくり返し消費されるために GC が頻繁に行われることになるため、多数のスレッドを利用するアプリケーションでは -Xconcurrentio によるチェックが必要です。

Java を使った Solaris のスレッド設定について詳細は、http://java.sun.com/docs/hotspot/threads/threads.html を参照してください。(英語のみ)

メモリの管理と割り当て

アプリケーションの効率的な実行は、メモリとガベージコレクションをいかに効率的に管理するかにかかっています。次に、メモリ管理機能と割り当て機能の最適化について説明します。

ガベージコレクタのチューニング

ガベージコレクションは、オブジェクトに割り当てられ、不要になったヒープ領域を回収します。不要オブジェクトを特定し、削除するプロセスはすべてのアプリケーションに影響し、25% ものスループットが消費されます。

ほとんどすべての Java 実行時環境には、世代別メモリシステムと洗練されたガベージコレクションメカニズムが用意されています。世代別メモリシステムでは、ヒープが複数の「世代」に区切られます。各世代のサイズは、慎重に決定されます。集積したオブジェクトによってメモリ容量が少なくなると、ガベージコレクションが強制的に実行されます。世代別メモリシステムの効率は、ほとんどのオブジェクトの生存期間が短いという事実に基づいています。ヒープ領域は、Old 世代と New 世代に分けられます。

New 世代には、新しいオブジェクトの領域 (Eden) と 2 つの Survivor 領域があります。新しいオブジェクトは、Eden に割り当てられます。長く生存するオブジェクトは、New 世代から Old 世代に移動されます。

New 世代では、2 つの下位領域 (2 つの Survivor 領域) と Eden を使った高速コピーによるガベージコレクションが行われ、生存し続けているオブジェクトは、一方の Survivor 領域からもう一方の Survivor 領域にコピーされます。New 世代で何回ものガベージコレクションを経験しても生存し続けたオブジェクトは、古い世代に移動されます。古い世代はサイズが大きく、簡単にいっぱいになることはありません。このため、ガベージコレクションの頻度は低く、1 回のガベージコレクションには、New 世代の領域だけで行われるコレクションより長い時間が必要となります。Old 世代の領域で行われるガベージコレクションは、フルガベージコレクション (フル GC) とも呼ばれます。

頻繁に行われる New 世代領域でのガベージコレクションは高速で (数ミリ秒)、たまに行われる フル GC は比較的時間がかかります (ヒープサイズに応じて数 10 ミリ秒から数秒)。

トレインアルゴリズムなど、その他のガベージコレクションアルゴリズムはインクリメンタル (増分的) です。フル GC はいくつかの増分区分に分断されます。このため、フル GC の実行中も、高速ガベージコレクションによる短時間の中断が生じる可能性が高くなります。これは、オーバーヘッドを伴うため、企業向けの Web アプリケーションでは使われません。

New 世代がいっぱいになると、Survivor 領域のオブジェクトを Old 世代に移動するための小規模なコレクションが行われます。Old 世代がいっぱいになると、オブジェクトヒープ全体を対象とした大規模なコレクションが行われます。

HotSpot と Solaris JDK は、どちらもスレッドローカルなオブジェクト割り当てプールを使って、ロックされず、高速でスケーラブルなオブジェクト割り当てを行います。旧世代の Java 仮想マシンでは、ユーザーアプリケーションレベルのオブジェクトプーリングがより効率的でした。オブジェクトの構築に非常に手間がかかり、しかも重要と考えられる場合にだけ、実行プロファイルでのプールを検討します。

ガベージコレクションのチューニングについて詳細は、http://java.sun.com/docs/hotspot/gc/index.html を参照してください。

ガベージコレクションの追跡

ガベージコレクションのパフォーマンスは、主にスループットとポーズによって測定されます。スループットは、ガベージコレクション以外のアクティビティが行われた合計時間の割合です。

ポーズは、ガベージコレクションによってアプリケーションが応答不能のようになる状態です。ガベージコレクションの要件は、ユーザーごと異なります。サーバーを多用するアプリケーションでは、スループットを測定対象とするかもしれませんが、グラフィカルプログラムは短いポーズでも機能しなくなることがあります。これ以外にも、フットプリントと即応性が重要な指標となります。

フットプリント

フットプリントはプロセスの作業セットで、ページとキャッシュ行単位で測定されます。即応性は、オブジェクトが不要になってから、メモリが解放されるまでの時間です。分散システムでは、これが重要になります。

各世代のサイズを決定するには、これらの要素を組み合わせて考える必要があります。New 世代を大きくすれば、スループットを最大化できるかもしれません。しかし、フットプリントと即応性は犠牲にされます。New 世代を小さくすることで、ポーズを最小化できますが、コレクションの頻度は高まります。

世代のコレクションによるポーズは、Java 仮想マシンの診断出力で確認できます。コマンド行に引数 -verbose:gc を指定すると、コレクションのたびに情報が出力されます。次に、このフラグを Java 仮想マシンに渡したときに出力される情報の例を示します。

[GC 50650K->21808K(76868K), 0.0478645 secs]
[GC 51197K->22305K(76868K), 0.0478645 secs]
[GC 52293K->23867K(76868K), 0.0478645 secs]
[Full GC 52970K->1690K(76868K), 0.54789968 secs]

矢印の前後の数字は、コレクションの前後に生存していたオブジェクトの合計サイズを示します。カッコ内の数字は合計空き容量を示します。これは、ヒープの合計容量からいずれかの Survivor 領域を差し引いたものです。この例では、3 回の小規模なコレクションと 1 回の大規模なコレクションが行われました。最初の GC では、コレクション前に生存していたオブジェクトは 50650 キロバイトで、コレクション後は 21808 キロバイトになりました。つまり、28842 キロバイトの不要オブジェクトが回収されたことになります。ヒープの合計サイズは 76868 キロバイトです。コレクションの処理には、0.0478645 秒かかりました。

ガベージコレクタのその他の設定

クラスを動的に生成し、ロードするアプリケーションでは、Old 世代は GC のパフォーマンスに関連しません。クラスを動的に生成し、ロードするアプリケーション (JSP) では、Old 世代がいっぱいになることでフル GC が行われる場合に、Old 世代が GC のパフォーマンスに影響を及ぼします。-XX:MaxPermSize オプションを使って、Old 世代を最大化します。

アプリケーションがガベージコレクタと対話するには、System.gc() 呼び出しを使ってコレクションを明示的に実行します。ただし、大規模なコレクションが強制されたり、大規模システムのスケーラビリティが損なわれるため、リソースの管理をアプリケーションに委ねることはお勧めできません。これを無効化するには、-XX:+DisableExplicitGC フラグを使います。

Sun ONE Application Server は、管理モジュールの RMI を使って監視を行います。RMI ベースの分散アプリケーションでは、たまに行われるローカルコレクション以外のガベージコレクションは行われません。このため、RMI は定期的なフル GC を強制的に実行します。このコレクションの頻度は、-sun.rmi.dgc.client.gcInterval プロパティを使って制御できます。たとえば、 - java -Dsun.rmi.dgc.client.gcInterval=3600000 と指定すると、デフォルトの 1 分おきではなく、1 時間おきに明示的なコレクションが行われます。

Java 仮想マシンの属性を指定する方法は、次のとおりです。

  • server.xml を編集して <jvm-config>vm tunable</jvm-config> を追加します。この vm tunable は、適用する属性を示します。
  • 管理インタフェースの JVM 設定で JVM オプションを設定します。

Java ヒープのチューニング

ここでは、パフォーマンスを向上するための Java ヒープのチューニングについて説明します。

Java ヒープのサイズ設定について

ヒープのサイズは、さまざまなパラメータを使って制御できます。

-Xms パラメータと -Xmx パラメータは、ヒープの最大サイズと最小サイズを指定します。各世代の領域がいっぱいになるとコレクションが行われるため、スループットは使用可能メモリの量に反比例します。デフォルトでは、JVM はコレクションのたびにヒープサイズを調節し、空き容量と生存オブジェクト容量の比率を特定の範囲内に収めようとします。この範囲は、-XX:MinHeapFreeRatio=<minimum> パラメータと -XX:MaxHeapFreeRatio=<maximum> パラメータを使ってパーセント単位で設定されます。合計サイズは、-Xms-Xmx によって制限されます。

サーバー側のアプリケーションでは、-Xms-Xmx の値に同じ固定のヒープサイズを指定します。ヒープのサイズが変動すると、JVM は事前に定義されている NewRatio を維持するために、New 世代と Old 世代のサイズを計算しなおします。

NewSize パラメータと MaxNewSize パラメータは、New 世代の最小サイズと最大サイズを制御します。2 つのパラメータに同じ値を設定することで、New 世代のサイズを固定できます。New 世代のサイズを大きくするほど、小規模なコレクションの実行回数は少なくなります。デフォルトでは、New 世代は NewRatio によって制御されます。たとえば -XX:NewRatio=3 と設定すると、New 世代と Old 世代の比率が 1:3 になり、Eden 領域と Survivor 領域の合計サイズがヒープ全体の 4 分の 1 になります。安全のため、NewSizeMaxNewSize には同じ値を設定します。

デフォルトでは、Sun ONE Application Server は Java HotSpot Server JVM を使って起動されます。Server JVM のデフォルトの NewRatio は 2 で、New 世代がヒープ全体の 3 分の 1、Old 世代が 3 分の 2 を占めます。New 世代を大きくするほど存在期間の短い多数のオブジェクトに対応でき、処理に時間のかかる大規模なコレクションの回数を減らせます。同時に、Old 世代のサイズも、存在期間の長いオブジェクトを保持できる程度の大きさがあります。

Java ヒープのサイズを決定するときは、次の点に注意してください。

  • JVM に割り当てることができるメモリの総量を決定します。New 世代のサイズを決定するための独自のパフォーマンス測定対象をグラフにして、最適な設定を求めます。
  • New 世代には十分なメモリを割り当てます。1.4 のデフォルト値は、NewRatio-Xmx の設定から計算されます。
  • New 世代の Eden 領域を大きくするほど、フルガベージコレクションの実行間隔は長くなります。ただし、それに比例して New 世代でのコレクションに時間がかかるようになります。一般に、Eden のサイズは最大ヒープサイズの 4 分の 1 から 3 分の 1 に設定します。
  • 通常は、New 世代より Old 世代を大きく設定する必要があります。

Survivor 領域の比率の設定

SurvivorRatio パラメータは、2 つの Survivor 領域のサイズを制御します。たとえば -XX:SurvivorRatio=6 と設定すると、各 Survivor 領域と Eden 領域の比率は 1:6 になり、それぞれの Survivor 領域のサイズは New 世代全体の 8 分の 1 となります。JDK 1.4 では、Solaris のデフォルトは 32 です。Survivor 領域が小さすぎると、高速コピーによるコレクションがオーバーフローし、その分が Old 世代に送られてしまいます。Survivor 領域が大きすぎると、この領域は空になってしまいます。ガベージコレクションのたびに、JVM は Old 世代に移動するまでにオブジェクトをコピーする回数のしきい値を選択します。

このしきい値は、Survivor 領域の使用容量が半分になる値に設定されます。

このしきい値と、New 世代のオブジェクトのコピー回数を表示するときは、-XX:+PrintTenuringDistribution オプションを使います。アプリケーションのライフタイムの分布を把握しておくと役立ちます。

最新のデフォルト設定を確認するときは、http://java.sun.com/docs/hotspot/VMOptions.html を参照してください。(英語のみ)

Solaris でのヒープ設定の例

次に、Solaris 環境での負荷の大きいサーバー中心型のアプリケーション用の Sun ONE Application Server のヒープ設定例を示します。これは、 server.xml ファイルに設定されます。

<jvm-options> -Xms3584m </jvm-options>

<jvm-options> -Xmx3584m </jvm-options>

<jvm-options> -verbose:gc </jvm-options>

<jvm-options> -Dsun.rmi.dgc.client.gcInterval=3600000 </jvm-options>

Windows でのヒープ設定の例

次に、Windows 環境での負荷の大きいサーバー中心型のアプリケーション用の Sun ONE Application Server のヒープ設定例を示します。これは、 server.xml ファイルに設定されます。

<jvm-options> -Xms1400m </jvm-options>

<jvm-options> -Xmx1400m </jvm-options>

HotSpot 仮想マシンのチューニングオプション

HotSpot は、Java アプリケーションのパフォーマンスを向上するための、「その場限りの」バイトコードコンパイラです。これを細かくチューニングすることで、パフォーマンスを向上できます。Sun ONE Application Server を使うときは、HotSpot のマニュアルを参照し、JVM が適切にチューニングされていることを確認してください。

次の Web ページを参照することをお勧めします。


前へ      目次      索引      次へ     
Copyright 2002 Sun Microsystems, Inc. All rights reserved.