![]() ![]() ![]() ![]() |
Oracle JRockit JVM の 1 つの大きな利点は、Just-In-Time (JIT) コンパイル型の JVM であることです (コードのコンパイル方式については「JRockit JVM によりコードのコンパイル方法」を参照)。つまり、JVM はメソッドを最初に実行するときに、そのメソッドをマシン コードにコンパイルします。JVM は、最初に呼び出されたときにすべてのクラスをネイティブ コードにコンパイルします。これにより、多くの新しいメソッドがコンパイルされた場合はアプリケーションの実行速度が低下します。しかし、メソッドが 2 回目に呼び出されたときには、実行が高速になります。頻繁に実行されるメソッドは後から JRockit JVM によって再コンパイルされるため、それ以降の実行ではいっそう最適化されて、アプリケーションがはるかに高速に動作するようになります。
この節では、起動の遅い JVM を見分けて、それに対処する方法について説明します。以下の内容について説明します。
アプリケーションの起動直後に動作が遅くなる問題には、多くの原因が考えられます。
別の JVM から JRockit JVM に最近切り替えた場合に、JVM の起動が遅いと感じることがあります。特に、開発用のサードパーティの JVM から、プロダクション用の JVM として JRockit JVM に切り替えた場合はこれが顕著になります。実際には、起動が遅いように見えても、それが標準的な動作です。JRockit JVM は、長時間実行されるアプリケーションと共に使用するように設計されています。そのため、コードがコンパイルされて最適化される分だけ起動に時間がかかります。JVM の起動が遅いわけではなく、単に起動時に処理する情報が多いだけです。いったんすべてのメソッドがコンパイルされれば、JVM ははるかに高速に動作するようになります。
起動時にアプリケーションが大量のメソッドをコンパイルしているかどうかを確認するには、-Xverbose:codegen
オプションを指定してアプリケーションを起動します。
このオプションを設定すると、名前、メモリの位置、コンパイルの持続時間、コンパイル開始からの経過時間など、コンパイルされているメソッドに関する情報が表示されます。コード リスト 26-1 に -Xverbose:codegen
のプリントアウトの例を示します。
[codegen] #775 1 (0x2) n jrockit/memory/AtomicInt.<init>(I)V
[codegen] #775 1 (0x2) n @0x7D62ED90-0x7D62ED9C 0.09 ms (277.99 ms)
[codegen] #776 1 (0x2) n jrockit/memory/AtomicInt.set(I)V
[codegen] #776 1 (0x2) n @0x7D62EDA0-0x7D62EDAA 0.08 ms (278.06 ms)
逆に言えば、起動時に大量のメソッドをコンパイルする必要がある場合は、そうでない場合に比べてかなりの起動時間がかかります。
いったん JIT コンパイルが完了すれば、メソッドはコンパイルされて実行にあまり時間がかからなくなります。ただし、JRockit JVM はアプリケーションの実行中に頻繁に使用されるメソッドを継続的に最適化するため、ときどき JVM の動作が遅いように見えることがあります。
データ ファイルなど、特定のファイルを検索するアプリケーションがある場合にも、起動時間が長くなることがあります。起動が遅い原因がアプリケーションにあると思われる場合は、JRA 記録を作成し、JRockit Runtime Analyzer でアプリケーション データを分析してみます。このツールは Oracle JRockit Mission Control で含まれています。
JRockit JVM R27.1 以降を JRockit Mission Control 2.0 以降と共に実行している場合は、JRA 記録を作成および解釈するための詳細な手順が Oracle JRockit Mission Control オンライン ヘルプに記載されています。
アプリケーションの内部でタイミングを測定するために、アプリケーションで System.nanoTime()
および System.currentTimeMillis()
メソッドを使用できます。アプリケーションでこれらのメソッドを使用すると、実行時にリソースを占有することになりますが、パフォーマンスへの影響はわずかです。
このメソッドは、最も正確なシステム タイマーを使用して、単調なタイマー値を返します。戻り値はナノ秒単位ですが、タイマーの実際の時間単位は OS とハードウェアの間で異なる可能性があります。タイマー値を関連させるための通常のゼロ点はありません。したがって、有意なデータを得るには、少なくとも 2 回、時間を取得する必要があります。
nanoTime()
では、オペレーティング システムごとに異なるメソッドを使用します。
タイマーの時間単位 (および、Linux では、時間値の取得に使用されるメソッド) に関する情報を取得するには、-Xverbose:timing
オプションを指定して JRockit JVM を起動します。
Windows での冗長タイミング レポートの例は次のとおりです。
[INFO ][timing ] Counter timer using resolution of 1779720000Hz
このメソッドは、現在の時刻をミリ秒単位で返します。現在の時刻は 1970 年 1 月 00:00:00 UTC からの時間として定義されます。
JVM の起動時に System.currentTimeMillis()
および System.nanoTime()
の値を取得するには、コマンドライン オプション -Xverbose:starttime
を使用します。starttime
の冗長出力は次のようになります。
[startti] VM start time: 1152871839957 millis 171588375730523 nanos
millis
値は System.currentTimeMillis()
が提供する値と同じ値、nanos
値は System.nanoTime()
が提供する値と同じ値です。
ここでは、起動が遅い場合に取りうる解決策について説明します。
コマンド ライン オプションを使用して JVM をチューニングする方法に問題がある場合もあります。起動を高速化するために JVM をチューニングする際のヒントについては、「JVM の起動を高速化するチューニング」を参照してください。
まれに、最適化が原因で起動が遅くなることがあるため、その可能性を除外してから他の解決策に取りかかる必要があります。
問題の原因が最適化にあると思われる場合は、-XnoOpt
起動コマンドを指定して JVM を起動し、最適化を完全に無効にします。このコマンドを指定すると、JVM はコードの最適化を一切行わなくなります。
-XnoOpt
を指定して実行すると JRockit JVM の起動が速くなる場合は、最適化の問題が発生していると考えられます。Oracle サポートに問題を報告してください。
回避策として、最適化に時間がかかりすぎるメソッドを除外してみることができます。そのためには、print_threads
Ctrl-Break ハンドラを使用して、スレッド ダンプを作成します (詳細については「診断コマンドの実行」を参照)。この出力から、最適化の問題の原因となっているメソッドを特定します。次に、「opt ファイル」を使用して、そのメソッドを最適化処理から削除します (詳細については「opt ファイルを作成、使用する」を参照)。
Java アプリケーションの問題が原因で起動が遅いと判断した場合は、その問題の原因をアプリケーションの観点から調査する必要があります。多くは、不必要な同期や不十分な同期リソースのしわ寄せを受けているメソッドに問題があると考えられます。ボトルネックの原因となっているメソッドを特定し、可能であれば Java アプリケーションのコードを書き直します。
各メソッドのコードの生成に時間がかかりすぎる原因が Oracle JRockit JVM にあると思われる場合や、「JVM の起動を高速化するチューニング」に示されているどの方法を試しても問題が解決しない場合は、Oracle サポートに連絡する必要があります。Oracle に問題を報告する方法、およびその際に提出する情報については、「Oracle サポートへのトラブル レポートの送付」を参照してください。
![]() ![]() ![]() |