10 ガベージファースト・ガベージ・コレクタのチューニング

この項では、ユーザーの要件を満たさない場合にガベージファースト・ガベージ・コレクタ(G1 GC)の動作を適応する方法を説明します。

G1の一般的な推奨事項

一般的な推奨事項は、G1をデフォルト設定で使用することです。異なる一時停止時間目標を設定したり、必要に応じて-Xmxを使用して、最大Javaヒープ・サイズを設定することもあります。

G1のデフォルト設定は、他のコレクタと異なってバランスが取られています。デフォルト構成のG1の目標は、最大スループットでもなく、最低のレイテンシでもなく、高いスループットで相対的に小さな統一された一時停止を行うことです。ただし、ヒープおよび一時停止時間の制御において領域を増分的に回収するG1のメカニズムにより、アプリケーション・スレッドにおいても、領域回収の効率性においてもオーバーヘッドが生じます。

高いスループットを優先する場合、-XX:MaxGCPauseMillisを使用して一時停止時間目標を緩くするか、より大きいヒープを提供します。レイテンシが主要な要件である場合、一時停止時間ターゲットを変更します。若い世代のサイズはG1が一時停止時間を満たすことを可能にする主要な手段であるため、-Xmn-XX:NewRatioなどのオプションを使用することによって若い世代のサイズを特定の値に制限することを回避します。若い世代のサイズを単一の値に設定すると、一時停止時間の制御をオーバーライドし、実際に無効化します。

他のコレクタからG1への移行

一般的に、他のコレクタ、特にコンカレント・マーク・スイープ・コレクタからG1に移行する際に、ガベージ・コレクションに影響するオプションをすべて削除して開始し、-Xmxとオプションで-Xmsを使用して、一時停止時間目標と全体のヒープ・サイズのみを設定します。

特定の方法で応答する他のコレクタに便利な多数のオプションは、まったく影響を及ぼさないか、スループットを低減するか、一時停止時間ターゲットを満たす可能性を小さくします。たとえば、若い世代のサイズを設定して、一時停止時間目標を満たすようにG1が若い世代のサイズを調整しないようにできます。

G1パフォーマンスの向上

G1は、追加のオプションを指定することなく、全体的に適切なパフォーマンスを提供するように設計されています。ただし、デフォルトのヒューリスティックまたはデフォルト構成が最適以下の結果になる場合があります。この項では、これらの場合における診断および改善に関するガイドラインを示します。このガイドでは、特定のアプリケーションの場合の、選択したメトリックにおいてガベージ・コレクタのパフォーマンスを向上させるためにG1が提供する可能性についてのみ説明します。場合によっては、アプリケーションレベルの最適化のほうが、(寿命が長くないオブジェクトでの問題のある状況の回避など)パフォーマンスを改善するようにVMをチューニングするよりも効果的なことがあります。

診断目的で、G1では包括的なロギングを提供しています。-Xlog:gc*=debugオプションを使用して、必要に応じて出力を調整することをお薦めします。ログでは、ガベージ・コレクション・アクティビティの一時停止中および一時停止外の詳細情報が提示されます。これには、コレクションのタイプおよび一時停止の特定のフェーズに費やされた時間の内訳が含まれています。

次の項で、共通のパフォーマンスの問題について説明します。

フル・ガベージ・コレクションの監視

フル・ヒープ・ガベージ・コレクション(フルGC)は、時間がかかることがあります。古い世代の高いヒープ占有率によって発生したフルGCは、ログ内の一時停止のフル(割当ての失敗)を見つけることにより、検出できます。通常、フルGCの前に、to-space exhaustedタグで示される退避の失敗が発生したガベージ・コレクションが発生します。

フルGCが発生する理由は、回収できなくなるほど多数のオブジェクトをアプリケーションが割り当てるためです。コンカレント・マーキングは領域回収フェーズの開始に間に合うように完了できないことがあります。フルGCに移行する確率は、多数の大型オブジェクトの割当てにより大きくなることがあります。G1におけるこれらのオブジェクトの割当て方法よっては、想定よりも時間がかかることがあります。

目標は、コンカレント・マーキングが時間どおりに終了することを確認するためのものです。これを実現するには、古い世代の割当て率を減らすか、コンカレント・マーキングの完了に時間をかけるようにします。

G1には、このような状況により適切に対処するためのオプションがいくつかあります。

  • Javaヒープ上で大型オブジェクトが占有しているリージョンの数を調べるには、gc+heap=infoロギングを使用します。 「Humongous regions: X->Y」行のYは、大型オブジェクトが占有しているリージョンの量を示しています。この数が古いリージョンの数よりも大きい場合、最適なオプションはこのオブジェクトの数を減らしてみることです。これを実現するには、  -XX:G1HeapRegionSizeオプションを使用してリージョン・サイズを増やします。現在選択されたヒープ・リージョン・サイズは、ログの開始時に出力されます。
  • Javaヒープのサイズを増やします。これは、通常、マーキングが完了する必要がある時間を増やします。
  • -XX:ConcGCThreadsを明示的に設定して、コンカレント・マーキング・スレッド数を増やします。
  • G1でマーキングを早く開始するように強制します。G1では、より早いアプリケーションの動作に基づいた開始ヒープ占有率(IHOP)しきい値を自動的に判定します。アプリケーションの動作が変化すると、これらの予測が当てはまらない場合があります。2つのオプションがあります: -XX:G1ReservePercentを変更して適応型IHOPの計算で使用されるバッファを増やすことにより、領域回収の開始タイミング用にターゲット占有率を低くするか、-XX:-G1UseAdaptiveIHOP-XX:InitiatingHeapOccupancyPercentを使用して手動設定により、IHOPの適応型計算を無効にします。

割当て失敗以外の原因によるフルGCの発生は、通常、アプリケーションまたは一部の外部ツールがフル・ヒープ・コレクションの原因であることを示します。原因がSystem.gc()であり、アプリケーション・ソースを変更できない場合、フルGCの影響は-XX:+ExplicitGCInvokesConcurrentを使用すると、低減できます。あるいは、-XX:+DisableExplicitGCを設定すると、VMにこれらを完全に無視させることもできます。外部ツールはフルGCを強制することがあります。これらを削除するには、フルGCをリクエストしないようにします。

大型オブジェクトの断片化

連続的なリージョン・セットを見つける必要があるため、すべてのJavaヒープ・メモリーが枯渇する前に、フルGCが発生することがあります。この場合に考えられるオプションは、オプション-XX:G1HeapRegionSizeを使用してヒープ・リージョン・サイズを増やして大型オブジェクト数を減らすか、ヒープ・サイズを増やすことです。極端な場合、使用可能なメモリーが十分にあっても、G1がオブジェクトを割り当てるのに使用可能な連続的な領域が十分にない可能性があります。これにより、フルGCで連続的な領域を十分に回収できないと、VMが終了します。したがって、前述のように、大型オブジェクトの割当て量を減らすか、ヒープを増やす以外の選択肢はありません。

レイテンシのためのチューニング

この項では、一時停止時間が長すぎるなど、共通のレイテンシの問題がある場合に、G1動作を改善するヒントについて説明します。

通常と異なるシステムまたはリアル・タイムの使用状況

ガベージ・コレクションの一時停止ごとに、gc+cpu=infoのログ出力には、一時停止時間中に費やされた時間の内訳とともにオペレーティング・システムの情報を記載する行が含まれています。このような出力の例は、User=0.19s Sys=0.00s Real=0.01sです。

ユーザー時間はVMコードで費やされた時間で、システム時間はオペレーティング・システムで費やされた時間で、リアル・タイムは一時停止時に経過した絶対時間です。システム時間が比較的高い場合、たいていは環境に原因があます。

システム時間が長い場合の共通の既知の問題は、次のとおりです。

  • VMがオペレーティング・システムのメモリーからメモリーを割り当てたり、戻したりすると、不要な遅延が発生することがあります。遅延を回避するには、オプション-Xms -Xmxを使用して最小および最大ヒープ・サイズを同じ値に設定し、-XX:+AlwaysPreTouchを使用してすべてのメモリーをプレタッチしてこの作業をVM起動フェーズに移動します。
  • 特にLinuxでは、透過的huge pages (THP)機能によって小さなページを大きなページに結合すると、一時停止時だけでなく、ランダムにプロセスの速度が低下することがあります。VMが大量のメモリーを割り当てたり保持したりするため、長時間VMの処理速度が低下するリスクが通常よりも高くなります。透過的huge pages機能を無効にする方法は、オペレーティング・システムのドキュメントを参照してください。
  • ログ出力に書き込むと、ログが書き込まれるハード・ディスクのすべてのI/O帯域幅を断続的に占有するバックグラウンド・タスクのためにしばらくの間速度が低下することがあります。ログまたは他のストレージ(メモリー指定されたファイル・システムなど)に別のディスクを使用して、これを回避することを検討してください。

リアル・タイムが他の合計時間よりも長くなる場合がありますが、これは、過負荷になった可能性のあるマシンで十分なCPU時間をVMが得られなかったことを示すことがあります。

処理に長時間かかる参照オブジェクト

参照オブジェクトの処理にかかる時間に関する情報は、Reference Processingフェーズに示されています。Reference Processingフェーズ時に、G1では特定タイプの参照オブジェクトの要件に従って、参照オブジェクトの参照を更新します。デフォルトでは、-XX:ReferencesPerThread個の参照オブジェクトごとに1つのスレッドを開始し、スレッド数は-XX:ParallelGCThreadsの値で制限されるというヒューリスティックを使用して、G1がReference Processingのサブフェーズを並列化します。このヒューリスティックを無効にするには、-XX:ReferencesPerThreadをゼロ(0)に設定して、デフォルトで使用可能なすべてのスレッドを使用するか、-XX:-ParallelRefProcEnabledで並列化を完全に無効化します。

時間がかかりすぎる若い世代のみのフェーズでの若い世代のみのコレクション

通常の若い世代のコレクション、すなわち一般的に若いコレクションは、若い世代のサイズ(具体的に言うと、コピーする必要のあるコレクション・セット内のライブ・オブジェクト数)に比例して時間がかかります。退避コレクション・セットフェーズが特にオブジェクト・コピー・サブフェーズで時間がかかりすぎる場合、-XX:G1NewSizePercentを減らします。これによって若い世代の最小サイズが小さくなり、一時停止が短くなる可能性があります。

アプリケーション・パフォーマンスや、特にコレクションを経て存続するオブジェクトの量が突然変化した場合、若い世代のサイズに関する別の問題が発生することがあります。これによって、ガベージ・コレクションの一時停止時間が大幅に増加する場合があります。-XX:G1MaxNewSizePercentを使用すると、若い世代の最大サイズの削減に役立つ場合があります。これは若い世代の最大サイズおよび一時停止時に処理する必要のあるオブジェクト数を制限します。

時間がかかりすぎる混合コレクション

混合コレクションは、古い世代の領域を回収する際に使用します。混合コレクションのコレクション・セットには若い世代と古い世代のリージョンが含まれています。gc+ergo+cset=traceログ出力を有効にすると、若い世代または古い世代のいずれかのリージョンの退避が一時停止時間に影響した時間に関する情報を取得できます。若い世代および古い世代のリージョンについては、それぞれ予測した若いリージョン時間と予測した古いリージョン時間を確認します。

予測した若い世代のリージョン時間が長すぎる場合、オプションは、「時間がかかりすぎる若い世代のみのフェーズでの若い世代のみのコレクション」を参照してください。これ以外の場合、古い世代のリージョンの一時停止時間への影響を少なくするために、G1では次の3つのオプションを提供しています。

  • -XX:G1MixedGCCountTargetを増やして、さらに多くのガベージ・コレクションで古い世代のリージョンの回収を拡大します。

  • -XX:G1MixedGCLiveThresholdPercentを使用して、リージョンを候補コレクション・セットに移動しないで、収集に比例的に時間がかかるリージョンを収集しないようにします。多くの場合、多く占有しているリージョンは収集に時間がかかります。

  • G1が多く占有しているリージョンを多数収集しないように、古い世代の領域回収を早めに停止します。この場合、 -XX:G1HeapWastePercentを増やします。

最後の2つのオプションは、現在の領域回収フェーズで領域を回収できる、コレクション・セットの候補リージョンの量を減らすことに注意してください。これは、持続した操作に対して古い世代の十分な領域をG1で回収できないことがあるという意味です。ただし、後の領域回収フェーズで、これらに対してガベージ・コレクションを実行できます。

高いRSの更新およびRSのスキャン時間

G1を有効にして古い世代のリージョンを退避させるには、G1ではリージョン間参照(リージョン間で相互に指す参照)の場所をトラッキングします。特定のリージョンを指すリージョン間参照のセットは、リージョンの記憶済セットと呼ばれます。記憶済セットは、リージョンの内容を移動する際に更新する必要があります。リージョンの記憶済セットの保守は、モーストリ・コンカレントです。アプリケーションが2つのオブジェクト間で新しいリージョン間参照を組み込んだとき、パフォーマンスのために、G1ではリージョンの記憶済セットを即座に更新しません。記憶済セットの更新リクエストは、効率性を高めるために遅延され、一括で実行されます。

G1ではガベージ・コレクションに完全な記憶済セットが必要であるため、ガベージ・コレクションのRSの更新フェーズでは未処理の記憶済セットの更新リクエストを処理します。RSのスキャン・フェーズにおいて記憶済セット内のオブジェクト参照を検索し、リージョンの内容を移動してから、これらのオブジェクト参照を新しい場所に更新します。アプリケーションによっては、これらの2つのフェーズではかなり時間がかかることがあります。

オプション-XX:G1HeapRegionSizeを使用してヒープ・リージョンのサイズを調整すると、リージョン間参照数と記憶済セットのサイズに影響があります。リージョンの記憶済セットの処理はガベージ・コレクション作業のかなりの部分を占めることがあるので、これは、達成可能な最大一時停止時間に直接影響を及ぼします。リージョンが大きくなると、リージョン間参照の数は少なくなるので、これらの処理に費やされる相対的な作業量が減少します。ただし、同時に、大きなリージョンはリージョンごとに退避するライブ・オブジェクトが増え、他のフェーズの時間が増加します。

G1では記憶済セットの更新の同時処理をスケジュールしようとするため、RSの更新フェーズは、許可された最大一時停止時間に対して約-XX:G1RSetUpdatingPauseTimePercentパーセントの時間がかかります。この値を減らすと、通常、G1はより多くの記憶済セットの更新作業を同時に実行します。

大きなオブジェクトを割り当てるアプリケーションと組み合せた、不要に高いRSの更新時間は、一括実行によって記憶済セットの同時更新作業を減らそうとする最適化によって発生することがあります。このような一括実行で作成されたアプリケーションがガベージ・コレクションの直前に発生した場合、ガベージ・コレクションはこの作業のすべてを一時停止のRSの更新時間部分で処理する必要があります。-XX:-ReduceInitialCardMarksを使用して、この動作を無効にし、このような状況を回避します。

RSのスキャン時間は、G1が記憶済セットのストレージ・サイズを小さくするために実行する圧縮量によっても決定されます。記憶済セットがメモリーに圧縮されて格納されると、ガベージ・コレクション時に格納された値の取得に時間がかかります。G1ではこのような圧縮(記憶済セットの粗大化と呼ぶ)を自動的に実行しますが、記憶済セットの更新はリージョンの記憶済セットの現在のサイズによって異なります。特に最も高い圧縮レベルでは、実際のデータの取得の処理速度が非常に遅くなることがあります。gc+remset=traceレベルのロギングと組み合せたオプション-XX:G1SummarizeRSetStatsPeriodでは、この粗大化が発生したかどうかを示します。この場合、Before GC SummaryセクションのDid <X> coarsenings行のXは高い値を示します。-XX:G1RSetRegionEntriesオプションの値を大幅に増やして、これらの粗大化の量を減らすことができます。このデータの収集に長時間かかることがあるため、本番環境でこの詳細な記憶済セットのロギングを使用しないでください。

スループットのためのチューニング

G1のデフォルト・ポリシーはスループットとレイテンシ間でバランスを取ろうとしますが、より高いスループットが望ましい場合があります。前述の項で説明した全体的な一時停止時間の短縮とは別に、一時停止の頻度を減らすことができます。これには、-XX:MaxGCPauseMillisを使用して最大一時停止時間を増やします。世代のサイズ設定のヒューリスティックでは自動的に若い世代のサイズを適応させ、これにより、一時停止の頻度を直接判定します。これによって期待したように動作しない場合、特に領域回収フェーズで、-XX:G1NewSizePercentを使用して若い世代の最小サイズを増やして、G1でこれを実行するように強制します。

場合によっては、若い世代の最小許容サイズの-XX:G1MaxNewSizePercentで若い世代のサイズを制限することにより、スループットを制限できます。gc+heap=infoロギングのリージョン・サマリー出力で確認することにより、これを診断できます。ここで、EdenリージョンとSurvivorリージョンを組み合せた割合は、リージョンの合計値の-XX:G1MaxNewSizePercentパーセントに近くなります。この場合、-XX:G1MaxNewSizePercentを増やすことを検討してください。

スループットを増やす別のオプションは、同時作業の量を減らそうとすることです。特に記憶済セットの同時更新は、大量のCPUリソースが必要になることがあります。-XX:G1RSetUpdatingPauseTimePercentを増やすと、作業が同時操作からガベージ・コレクションの一時停止に移動します。最悪の場合、-XX:-G1UseAdaptiveConcRefinement -XX:G1ConcRefinementGreenZone=2G -XX:G1ConcRefinementThreads=0を設定して、記憶済セットの同時更新を無効にできます。これにより、このメカニズムを無効にして、すべての記憶済セット更新作業を次のガベージ・コレクションの一時停止に移行します。

-XX:+UseLargePagesを使用して大きなページの使用を有効にして、スループットを向上させることもできます。大きなページの設定方法は、ご使用のオペレーティング・システムのマニュアルを参照してください。

これを無効にすると、ヒープのサイズ変更作業を最小化でき、オプション-Xms-Xmxを同じ値に設定します。さらに、-XX:+AlwaysPreTouchを使用して、オペレーティング・システムによる仮想メモリーと物理メモリーの作業をVMの起動時に移動します。これらの両方の措置は、一時停止をより一貫したものにするために特に望ましい場合があります。

ヒープ・サイズのためのチューニング

他のコレクタと同様に、G1では、ガベージ・コレクションで費やされる時間が-XX:GCTimeRatioオプションで決定される比率を下回るように、ヒープのサイズを設定しようとします。このオプションを調整して、G1がユーザーの要件を満たすようにします。

チューニング可能なデフォルト設定

この項では、このトピックで紹介されているコマンド行オプションのデフォルト値と追加情報について説明します。

表10-1 G1 GCのチューニング可能なデフォルト設定

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

-XX:+G1UseAdaptiveConcRefinement

-XX:G1ConcRefinementGreenZone=<ergo>

-XX:G1ConcRefinementYellowZone=<ergo>

-XX:G1ConcRefinementRedZone=<ergo>

-XX:G1ConcRefinementThreads=<ergo>

記憶済セットの同時更新(調整)では、これらのオプションを使用して、同時調整スレッドの作業の分散を制御します。G1では、これらのオプションにエルゴノミックな値を選択することにより、-XX:G1RSetUpdatingPauseTimePercent時間が、ガベージ・コレクションの一時停止で費やされ、残りの作業を処理し、適応させながら必要に応じてこれらを調整します。これによって極端な長い一時停止になることがあるため、慎重に変更してください。

-XX:+ReduceInitialCardMarks

これは、初期オブジェクトの割当てのために記憶済セットの同時実行(調整)作業を一括実行します。

-XX:+ParallelRefProcEnabled

-XX:ReferencesPerThread=1000

-XX:ReferencesPerThreadによって並列度が決まります。N個の参照オブジェクトごとに1つのスレッドがReference Processingのサブフェーズに追加され、スレッド数は-XX:ParallelGCThreadsで制限されます。値がゼロ(0)の場合、-XX:ParallelGCThreads値で指定されている最大数のスレッドが常に使用されます。 

これは、java.lang.Ref.*インスタンスの処理を複数のスレッドで並行で実行するかどうかを判定します。

-XX:G1RSetUpdatingPauseTimePercent=10 

これは、残りの記憶済セットを更新するRSの更新フェーズでG1が費やす必要があるガベージ・コレクションの合計時間の割合を判定します。G1では、この設定を使用して、記憶済セットの同時更新の量を制御します。

-XX:G1SummarizeRSetStatsPeriod=0 

これは、G1が記憶済セットのサマリー・レポートを生成する、多数のGCにおける期間です。無効にするには、これをゼロに設定します。記憶済セットのサマリー・レポートの生成はコストのかかる操作であるため、必要な場合にのみ、かなり高い値を設定してこれを使用してください。出力するには、gc+remset=traceを使用します。

-XX:GCTimeRatio=12 

これは、アプリケーションとは対照的に、ガベージ・コレクションで費やす必要がある時間の対象となる比率の除数です。ヒープを増やす前にガベージ・コレクションで費やすことができる時間の小数を判定するための実際の式は、1 / (1 + GCTimeRatio)です。このデフォルト値は、ガベージ・コレクションで費やされる時間の約8%の目標値になります。

-XX:G1PeriodicGCInterval=0

G1が定期的なガベージ・コレクションをトリガーする必要があるかどうかを確認する間隔(ミリ秒)。無効にするにはゼロに設定します。

-XX:+G1PeriodicGCInvokesConcurrent

設定されている場合は、定期的なガベージ・コレクションでコンカレント・マーキングがトリガーされるか、既存のコレクション・サイクルが継続され、設定されていない場合はフルGCがトリガーされます。

-XX:G1PeriodicGCSystemLoadThreshold=0.0 ホストのgetloadavg()コールで返される現在のシステム負荷のしきい値で、定期的なガベージ・コレクションをトリガーする必要があるかどうかを判定します。現在のシステム負荷がこの値よりも高いと、定期的なガベージ・コレクションは実行されません。値ゼロは、このしきい値チェックが無効であることを示します。

注意:

<ergo>は、実際の値が環境によってエルゴノミックに決定されるという意味です。