ドキュメント



Oracle Java SE Embedded: 開発者ガイド

15 チューニング

この章では、ジャスト・イン・タイムの(JIT)コンパイラのコンパイル済メソッドを格納するコードキャッシュ内のメモリー消費量を削減する手法について説明します。

この章は、次の項目で構成されています:

入門

Java Virtual Machine (JVM)は、ネイティブ・コードを生成し、コードキャッシュと呼ばれるメモリー領域に格納します。 JVMは、動的に生成されたインタプリタ・ループ、Java Native Interface (JNI)スタブ、およびジャスト・イン・タイム(JIT)コンパイラによってネイティブ・コードにコンパイルされるJavaメソッドなど、様々な理由でネイティブ・コードを生成します。 JITは、コードキャッシュの最大のユーザーです。 この付録では、良好なパフォーマンスを維持しながら、JITコンパイラのコードキャッシュの使用量を削減する方法を説明します。

この章では、JITのコードキャッシュの使用を減らす3つの方法について説明します:

  • JITで使用できるコードキャッシュの量を制限します。

  • JITをチューニングして、より少ないメソッドをコンパイルします。

  • メソッドごとに少ないコードを生成するようにJITをチューニングします。

java Launcher Codecacheオプションのサマリー

この項の表に示されているjavaランチャによって渡される「JVM」オプションを使用して、JITで使用されるコードキャッシュの量を減らすことができます。 表の説明はサマリーです。 ほとんどのオプションについては、以降のセクションで詳しく説明します。

javaコマンドのCodecacheオプションの使用方法

次のセクションに示すオプションは、次の特徴を共有します。

  • すべてのオプションは、-XXオプション(-XX:InitialCodeCacheSize=32mなど)です。 true/falseの値を持つオプションは、trueの場合は+、falseの場合は-を使用して指定します。 たとえば、-XX:+PrintCodeCacheはこのオプションをtrueに設定します。

  • "変化"がデフォルト値としてリストされているオプションについては、XX:+PrintFlagsFinalを指定してランチャを実行して、プラットフォームのデフォルト値を確認します。

  • オプションのデフォルト値が(クライアントまたはサーバー)を使用しているJVMによって異なる場合、両方のデフォルトが/'で区切られて表示されます。 クライアントJVMのデフォルトが最初にリストされます。 最小JVMは、クライアントJVMと同じJITを使用するため、デフォルトは同じです。

サイズ・オプション

「表15-1」は、コードキャッシュ・サイズ・オプションの概要を示します。 「コードキャッシュのサイズの制約」も参照してください。

表15-1 サイズ・オプション

オプション デフォルト 説明

InitialCodeCacheSize

160K (様々)

初期コード・キャッシュ・サイズ(バイト単位)

ReservedCodeCacheSize

32M/48M

予約済コード・キャッシュ・サイズ(バイト単位) - 最大コード・キャッシュ・サイズ

CodeCacheExpansionSize

32K/64K

コード・キャッシュ拡張サイズ(バイト単位)


Codecacheフラッシュ・オプション

「表15-2」は、コードキャッシュ・フラッシュ・オプションの概要を示します。

表15-2 Codecacheフラッシュ・オプション

オプション デフォルト 説明

ExitOnFullCodeCache

false

キャッシュがいっぱいになったらJVMを終了

UseCodeCacheFlushing

false

コンパイラをシャットダウンする前にコードキャッシュをスイープしてみてください

MinCodeCacheFlushingInterval

30

コードキャッシュ・スイープ・セッション間の最小秒数

CodeCacheMinimumFreeSpace

500K

指定した容量より少ない領域が残っている場合は、コンパイルを停止します。 この領域は、ネイティブ・アダプタなど、コンパイルされたメソッドではないコード用に予約されています。


コンパイル・ポリシーのオプション

「表15-3」は、コンパイル・ポリシーの(コンパイル時)オプションを要約します。

表15-3 コンパイル・ポリシーのオプション

オプション デフォルト 説明

CompileThreshold

1000または1500/10000

(re-)コンパイル前の解釈済メソッド呼出しの数

OnStackReplacePercentage

140から933

(re-)がOSRコードをコンパイルする前のNON_TIEREDメソッド呼出し数/(CompileThresholdのパーセンテージで表されます)の分岐数


コンパイル制限オプション

「表15-4」は、コンパイルされるコード量を決定するコンパイル制限オプションの概要を示します)。

表15-4 コンパイル制限オプション

オプション デフォルト 説明

MaxInlineLevel

9

インライン化されたネストされたコールの最大数

MaxInlineSize

35

インライン化するメソッドの最大バイトコード・サイズ

MinInliningThreshold

250

メソッドをインライン化する必要のある最小呼出し数

InlineSynchronizedMethods

true

インライン同期メソッド


診断オプション

「表15-5」は、診断オプションの概要を示します。

表15-5 診断オプション

オプション デフォルト 説明

PrintFlagsFinal

false

引数および人間工学処理の後にすべてのJVMオプションを出力

PrintCodeCache

false

終了時にコード・キャッシュのメモリー使用量を出力

PrintCodeCacheOnCompilation

false

メソッドがコンパイルされるたびに、コード・キャッシュのメモリー使用量を出力


Codecacheの使用量の測定

コードキャッシュの使用量削減作業の成功を測定するには、コードキャッシュの使用量とパフォーマンスへの影響を測定する必要があります。 この項では、コードキャッシュの使用方法を測定する方法について説明します。 アプリケーションのパフォーマンスを測定する最善の方法を決定するのは、ユーザー次第です。

ベースライン(コード・キャッシュの削減技術が適用されていないときに使用されるコードキャッシュの量)から開始し、コードカッシュの削減手法がベースラインに対するコードカッシュのサイズとパフォーマンスの両方に与える影響を監視します。

コードキャッシュは比較的小さく始まり、新しいメソッドがコンパイルされると必要に応じて大きくなることに注意してください。 コンパイルされたメソッドは、特にコードキャッシュの最大サイズが制約されている場合、コードキャッシュから解放されることがあります。 freeメソッドによって使用されるメモリーは、新しくコンパイルされたメソッドに再利用できるため、コードカッシュをさらに増やすことなく、追加のメソッドをコンパイルできます。

コードキャッシュの使用状況に関する情報は、javaランチャ・コマンドラインで-XX:+PrintCodeCacheを指定することで取得できます。 アプリケーションが終了すると、次のような出力が表示されます:

CodeCache: size=32768Kb used=542Kb max_used=542Kb free=32226Kb
 bounds [0xb414a000, 0xb41d2000, 0xb614a000] 
 total_blobs=131 nmethods=5 adapters=63 
 compilation: enabled

コード・キャッシュの削減作業のための出力の最も有用な部分は、最初の行です。 次に、出力される各値について説明します:

  • size: コードキャッシュの最大サイズ。 これは、-XX:ReservedCodeCacheSizeで指定されたものと同等である必要があります。 これは、コードカッシュによって使用される物理メモリー(RAM)の実際の量ではないことに注意してください。 これは、そのために確保されている仮想アドレス空間の量にすぎません。

  • used: 実際に使用されているメモリーの量。 これは通常、コードキャッシュが占めるRAMの量です。 ただし、断片化と、コードカッシュ内の空きブロックと割り当てられたメモリー・ブロックの混在により、コードカッシュがこの値で示されるよりも多くのRAMを占める可能性があります。これは、その時点で解放されたブロックがまだRAM内にある可能性があるためです。

  • max_used: これはコードキャッシュの使用のための高い水位標であり、コードキャッシュが成長して使用した最大サイズです。 これは通常、コードキャッシュによって占有されているRAMの量とみなされ、ある時点で使用されていたコードキャッシュに空きメモリーが含まれます。 このため、アプリケーションが使用しているコードキャッシュの量を決定する際に使用する番号です。

  • free: これは、sizeからusedを引いた値です。

-XX:+PrintCodeCacheOnCompilationオプションは、-XX:+PrintCodeCacheで生成された最初の行と同じ出力も生成しますが、メソッドがコンパイルされるたびに同じ出力を生成します。 これは、終了しないアプリケーションの測定に役立ちます。 また、アプリケーションの起動完了後など、アプリケーションの実行の特定の時点でのコードキャッシュの使用に関心がある場合に役立ちます。

max_usedは通常、コードキャッシュによって使用されるRAMの量を表すため、これはベースライン測定を行うときに注意する必要がある値です。 次の項では、max_usedを減らす方法について説明します。

コードキャッシュのサイズの制約

コードキャッシュのサイズを制限すると、コードキャッシュは、制約のないコードキャッシュが使用するものよりも小さいサイズに制限されます。 ReservedCodeCacheSizeオプションは、コードキャッシュの最大サイズを決定します。 デフォルトでは、クライアントJVMは32MB以上、サーバーVMは48MB以上になります。 ほとんどのJavaアプリケーションでは、このサイズが非常に大きいため、アプリケーションはコードキャッシュ全体を埋めることはありません。 したがって、コーデカチは制約なしとみなされ、JITはコンパイルする必要があると考えられるコードをすべてコンパイルし続けます。

コードキャッシュのサイズを制約すると便利なのはいつですか?

新しいメソッド・セットが"暑い"になるような状態変更を行うアプリケーションは、制約されたコードキャッシュから大きなメリットを得ることができます。

一般的な状態の変更は、起動から通常の実行までです。 アプリケーションは起動時に多くのコンパイルをトリガーする可能性がありますが、起動後にこのコンパイル済コードはほとんど必要ありません。 コードキャッシュを制限することで、起動時にコンパイルされたコードをスローするようにコードキャッシュ・フラッシュをトリガーし、アプリケーションの実行中に必要なコードを使用できるようにします。

一部のアプリケーションでは、実行中に状態が変更され、長時間新しい状態になる傾向があります。 これらのアプリケーションでは、コードキャッシュは、特定の状態に必要なコンパイル済コードを保持するのに十分な大きさである必要があります。 したがって、アプリケーションに5つの異なる状態があり、それぞれが適切に実行するために約1MBのコードキャッシュが必要な場合、コードキャッシュを1MBに制限できます。これは、アプリケーションの通常の5MBのコードキャッシュの使用量よりも80%削減されます。 ただし、アプリケーションが状態を変更するたびに、JITが新しい状態に必要なメソッドをコンパイルしている間に、パフォーマンスが低下することに注意してください。

コードキャッシュのサイズを制限する方法

コードキャッシュが(使用方法がReservedCodeCacheSizeに近づくか、)を制約してより多くのメソッドをコンパイルする場合、JITは最初にコンパイル済のいくつかのメソッドをスローする必要があります。 コンパイルされたメソッドの破棄は、コードキャッシュ・フラッシュと呼ばれます。 UseCodeCacheFlushingオプションは、キャッシュのフラッシュのオンとオフを切り替えます。 デフォルトではオンです。 この機能は、XX:-UseCodeCacheFlushingを指定して無効にできます。 有効にすると、コードキャッシュで使用可能なメモリーが少ないときにフラッシュがトリガーされます。 コードキャッシュを制限する場合は、コードキャッシュのフラッシュを有効にすることが重要です。 フラッシュが無効になっている場合、JITはコードキャッシュがいっぱいになったあとにメソッドをコンパイルしません。

アプリケーションに適したReservedCodeCacheSize値を決定するには、まず、コードキャッシュが制約されていないときにアプリケーションが使用するコードキャッシュの量を確認する必要があります。 「Codecacheの使用量の測定」で説明されているXX:+PrintCodeCacheオプションを使用し、max_used値(アプリケーションが使用するコードキャッシュの量)を調べます。 その後、ReservedCodeCacheSizeを小さい値に設定して、アプリケーションのパフォーマンスを確認できます。

小さい(5MB未満)コードキャッシュを使用する場合は、CodeCacheMinimumFreeSpaceを考慮する必要があります。 大きなコーデカルの場合、デフォルト値はそのままにします。 一般に、JITは、このオプションを尊重するのに十分なスペースをコードキャッシュに保持します。 小さいコード・キャッシュの場合は、新しいReservedCodeCacheSizeにCodeCacheMinimumFreeSpaceを追加します。 たとえば、次のようにします:

max_used = 3M
CodeCacheMinimumFreeSpace = 500k

コードキャッシュ・サイズを3MBから2MBに減らすには、ReservedCodeCacheSizeを2500k (2M+500K)に増やします。変更後、max_usedが2Mに変更されていることを確認します。

コードキャッシュを制約する場合は、通常、CodeCacheMinimumFreeSpaceを小さい値に設定できます。 ただし、CodeCacheMinimumFreeSpaceは100KB以上である必要があります。 空き領域が不足すると、JVMはVirtualMachineErrorをスローして終了するか、まれにクラッシュします。 3Mバイトから2Mバイトの例では、次の設定が適しています:

-XX:ReservedCodeCacheSize=2100k
-XX:CodeCacheMinimumFreeSpace=100k

ニーズに最適なReservedCodeCacheSizeを見つけることは、反復的なプロセスです。 アプリケーションのパフォーマンスが許容範囲外に低下し、許容可能なパフォーマンスが再度得られるまで、ReservedCodeCacheSizeに小さい値および小さい値を繰り返し使用できます。 また、達成している増分リターンも測定する必要があります。 わずか5%のパフォーマンス低下でmax_usedを50%削減でき、10%のパフォーマンス低下でmax_usedを60%削減できる場合があります。 この例では、2番目の10%のコードキャッシュの削減コストは、最初の50%のコードキャッシュの削減と同じくらいです。 この場合、コードキャッシュの使用量とパフォーマンスとのバランスが50%低下すると結論付けることができます。

コンパイルの削減

コンパイルされたメソッドの数、またはコンパイルされる速度を減らすことは、使用されるコード・キャッシュの量を減らすもう1つの有効な方法です。 積極的なメソッドのコンパイル方法に影響する2つの主要なコマンドライン・オプション: CompileThresholdおよびOnStackReplacePercentage CompileThresholdは、メソッドのコンパイル前に必要なメソッド呼出しの数に関連しています。 OnStackReplacePercentageは、メソッドがコンパイルされる前に取得された後方分岐の数に関連し、CompileThresholdの割合で指定されます。 メソッドの逆方向ブランチと呼出しの結合数がCompileThreshold * OnStackReplacePercentage / 100に達するか、それを超えると、メソッドがコンパイルされます。 BackEdgeThresholdというオプションもありますが、現在は何も行いません。 かわりにOnStackReplacePercentageを使用してください。

これらのオプションの値を大きくすると、コンパイルが減少します。 オプションをデフォルトよりも大きく設定すると、メソッドがコンパイルされるときに(または再コンパイル)が遅延し、メソッドがコンパイルされない可能性があります。 通常、これらのオプションを大きい値に設定すると、パフォーマンス(メソッドの解釈)も低下するため、パフォーマンスとコードキャッシュの両方の使用状況を調整するときに監視することが重要です。 クライアントJVMの場合、これらのオプションのデフォルト値のトリプルは適切な開始点です。 サーバーJVMでは、CompileThresholdはすでにかなり高い値に設定されているため、これ以上調整する必要はありません。

コンパイル済メソッド・サイズの削減

コンパイルされたメソッドのサイズを小さくするコマンドライン・オプションが多数ありますが、通常はパフォーマンス・コストがかかります。 他のセクションで説明したコードキャッシュの削減メソッドと同様に、キーは、パフォーマンスが大幅に低下することなく、コード・キャッシュの使用率を低下させる設定を見つけることです。

このセクションで説明するオプションはすべて、コンパイラが実行するインライン化の量を減らすことに関連しています。 インライン化とは、コンパイラが呼び出されたメソッドのコードを、コンパイルされるメソッドのコンパイルされたコードに組み込むことです。 インライン化は多くのレベルの深さで行うことができるため、メソッドa()c()をコールするb()をコールすると、a()をコンパイルするときにb()a()にインライン化できます。これにより、c()のインライン化をa()にトリガーできます。

JITコンパイラは、複数のヒューリスティックを使用して、メソッドをインライン化する必要があるかどうかを判断します。 一般に、ヒューリスティックは最適なパフォーマンスのためにチューニングされます。 ただし、それらのいくつかを調整して、より少ないコードキャッシュ使用のためにパフォーマンスを犠牲にすることができます。 チューニングするより便利なオプションを次に説明します:

InlineSmallCode

このオプションの値によって、コンパイル中のメソッドから呼び出されたときに、すでにコンパイルされているメソッドがインライン化される必要があるサイズが決まります。 このメソッドのコンパイル済バージョンがInlineSmallCodeの設定より大きい場合、インライン化されません。 かわりに、メソッドのコンパイル済バージョンへのコールが生成されます。

MaxInlineLevel

このオプションは、インライン化コール・チェーンの最大ネスト・レベルを表します。 インライン化は、より深いレベルではあまり役に立たなくなり、最終的にコードが膨らむためにパフォーマンスに悪影響を及ぼす可能性があります。 デフォルト値は9です。 MaxInlineLevelを1または2に設定すると、getterやsetterなどの簡単なメソッドがインライン化されます。

MaxInlineSize

このオプションは、インライン化されたメソッドの最大バイトコード・サイズを表します。 デフォルトは35ですが、各インライン・レベルで自動的に削減されます。 非常に小さい(約6)を設定すると、簡単なメソッドのみがインライン化されます。

MinInliningThreshold

インタプリタは、メソッド・コール・サイトで呼出し回数を追跡します。 このカウントは、呼び出されたメソッドをインライン化する必要があるかどうかを判断するためにJITによって使用されます。 メソッドの呼出し回数がMinInliningThresholdより少ない場合、メソッドはインライン化されません。 このしきい値を大きくすると、インライン化されるメソッド・コール・サイトの数が減少します。 簡易メソッドは、常にインライン化され、MinInliningThresholdの設定の対象にはなりません。

InlineSynchronizedMethods

このオプションを使用すると、synchronizedとして宣言されているメソッドのインライン化を無効にできます。 同期は、特にマルチ・コア・デバイスではかなり高価な操作であるため、小さい同期方式でもインライン化の利点は大幅に減少します。 同期メソッドのインライン化は、パフォーマンス低下がほとんどまたはまったく認識されず、コードキャッシュの使用量が著しく減少している場合に、無効にできます。

ウィンドウを閉じる

目次

Oracle Java SE Embedded: 開発者ガイド

展開 | 縮小