GCの一時停止に対処する方法

このトピックでは、ガベージコレクターの長い一時停止の原因とその対処方法について説明します。 CMS(低ポーズ)について説明します。現時点では、CMSは大容量のメモリと低遅延の要件を持つアプリケーションで最も一般的に使用されるアルゴリズムです。 この説明は、アプリケーションが、大量のメモリと多数のプロセッサを備えたボックス上で回転していることを前提にしています。



特にGCおよびCMSの操作の一般的な原則については、 ここで詳しく説明します 。 このトピックを理解するために必要なことをここで簡単に要約します。

したがって、この場合、アプリケーションが完全に停止する次の瞬間があります。
  1. マイナーガベージコレクション
  2. 初期化マークCMSフェーズ
  3. リマークフェーズCMS
  4. フルGC

最初に、これら4つのケースの一時停止が非常に大きく、アプリケーションの寿命を損なうかどうかを判断する必要があります。 あなたが必要とするまさにそのような条件があるので、誰もが生産システムを見る方が良いです。 次のパラメータを使用してJVMを起動することにより、これをパフォーマンスを低下させることなく完全に実行できることを神に感謝します。

-verbose:gc
-Xloggc:gc.log
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime

CMSログの読み方の詳細については、 こちらをご覧ください 。 jvmの開始からの相対的な秒数ではなく、人間的に時間を出力したい場合は、pythonで記述したパーサーを使用できます。 現在、世界のストップイベントに関するログの一部にのみ関心があります。

1.マイナーガベージコレクションが遅くなります。


[GC [DefNew: 209100K->25808K(235968K), 0.0828063 secs] 209100K->202964K(1284544K), 0.0828351 secs] [Times: user=0.02 sys=0.08, real=0.08 secs]
Total time for which application threads were stopped: 0.0829205 seconds

マイナーアセンブリの主なアルゴリズムはコピーであるため、YougGenのライブオブジェクトが多いほど、マイナーアセンブリの動作時間が長くなります。 一時停止が長すぎて、あなたが幸せでないとき、私は少なくとも3つの事柄を見なければなりません。

a。 JVMが不適切なアルゴリズムを使用しています。 上記のログは、シングルスレッドアルゴリズム(DefNew)を使用しています。 新しいマルチスレッドアルゴリズム(ログではParNewと呼ばれます)を試すことをお勧めします。これは、-XX:+ UseParNewGCパラメーターで強制的に有効にできます。 また、ログにPSYoungGenという名前が表示されている場合は、JVMが並列アルゴリズムを使用していることを意味しますが、古い実装です。 CMSと組み合わされていますが、利用できないようです。

b。 YoungGenに割り当てられたメモリが大きすぎます(指定されたログでは235968Kです)。 -Xmnパラメーターを設定することで削減できます。

c。 オブジェクトに許可された年齢の大きな大きすぎるサバイバースペースを使用しているため、オブジェクトがコピーされてOldGenにトラップされず、不必要なマイナーGC負荷が発生します。 この状況は、-XX:SurvivorRatioおよび-XX:MaxTenuringThresholdパラメーターを使用して修正できます。 このケースのより詳細な分析については、-XX:+ PrintTenuringDistributionパラメーターを指定してJVMを実行し、GCログ内のオブジェクトの生成に関する詳細情報を取得できます。

2. init-markは長い間機能します


[GC [1 CMS-initial-mark: 680168K(1048576K)] 706792K(1284544K), 0.0001161 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Total time for which application threads were stopped: 0.0002740 seconds

このフェーズでは、ガベージコレクターは、GCルートから直接アクセス可能なすべてのオブジェクトをマークします。 このフェーズに多くの時間がかかることがわかった場合は、おそらくローカル変数と静的フィールドからのリンクがたくさんあります。

ここでは、このフェーズに参加するスレッド(ParallelCMSThreads)の数を増やすことができます。 デフォルトでは、(ParallelGCThreads + 3)/ 4)として計算されます。 つまり ParallelGCThreads = 8の場合、2つのスレッドのみがinit-markフェーズに参加します。これは、並列処理によるオーバーヘッドのために特定の成長をもたらさない場合があります。

3.発言フェーズでの大きな一時停止


[GC[YG occupancy: 26624 K (235968 K)][Rescan (non-parallel) [grey object rescan, 0.0056478 secs][root rescan, 0.0001873 secs], 0.0059038 secs][weak refs processing, 0.0000090 secs] [1 CMS-remark: 750825K(1048576K)] 777449K(1284544K), 0.0059808 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Total time for which application threads were stopped: 0.0061668 seconds

マークフェーズでは、すべてのリンクの変更を監視する特別なプロセスが実行されます。 変更フェーズをすべて表示するには、コメントフェーズが必要です。

a。 ログに「Rescan(non-parallel)」というフレーズが表示される場合は、-XX:+ CMSParallelRemarkEnabledオプションを有効にして、このフェーズで複数のスレッドを有効にすることをお勧めします。

b。 週の参照はこのフェーズで正確にクリアされるので、使用する参照が多すぎるかどうかを確認します。 (例:java.util.WeakHashMap)

c。 おそらくあなたのリンクは大きく変化しています。 初期マークと発言の間の経過時間を確認します。 これらのフェーズ間で経過する時間が短いほど、変更されるリンクが少なくなり、注釈機能が速くなります。 リマークフェーズの直前の5番目のJavaから開始し、アボート可能プレクリーンフェーズが追加されましたが、これは本質的には何も行いません。 このロジックには2つの理由があります。 最初のコメントは、YoungGenもスキャンし、マルチスレッドモードで動作する可能性があるため、マイナーアセンブリが必要です。その後、YoungGenの残りのオブジェクトを並列処理用の領域に効果的に分割することが可能になります。 2つ目は非常に長い作業停止フェーズです。マイナービルドの直後に機能する場合は、1つの大きな長い一時停止が発生します。 この動作を制御できるパラメーターがいくつかあります:CMSScheduleRemarkEdenSizeThreshold、CMSScheduleRemarkEdenPenetration、CMSMaxAbortablePrecleanTime。 コメントの直前にマイナーアセンブリが呼び出されるCMSScavengeBeforeRemarkを試すことをお勧めします。 したがって、init-markとremarkの間の時間を可能な限り短縮し、remarkフェーズの作業が少なくなります。 これは、通常発生するマイナーアセンブリの一時停止が発言よりはるかに短い場合に特に効果的です。

4.ログ内のフルGC


(concurrent mode failure): 798958K->74965K(1048576K), 0.0270334 secs] 1033467K->74965K(1284544K), [CMS Perm : 3022K->3022K(21248K)], 0.0270963 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
Total time for which application threads were stopped: 0.0271630 seconds

これとFull GCを引き起こす他のいくつかのケースについては、 ここで詳細に説明しました

休止について言いたかったのはそれだけです。 はい、1つまたは2つのカーネルがない限り、増分CMS(-XX:+ CMSIncrementalMode)を使用しないでください。 すべてがゆっくりとしか機能しません。

そして、他のアルゴリズムについてのいくつかの言葉。

Garbage First(G1)、これはデフォルトでJava 7に表示され、オプション-XX:+ UnlockExperimentalVMOptionsおよび-XX:+ UseG1GCを使用して、Java SE 6 Update 14以降の6番目のJavaで有効にすることができます。 これは、エリア全体をメモリの小さなセクションに分割し、さまざまな時間セクションで収集できるようにすることで、非常に短い一時停止を実現するというものです。 メモリーを領域に分割することに基づいて、目的の一時停止を設定できるさまざまなJVMパラメーターがあります。 このアプローチは、その有効性がメモリ内のオブジェクトのトポロジに大きく依存するため、ユニバーサルと呼ぶことはできません。 オブジェクトがアプリケーション全体にリンクしているさまざまなキャッシュを積極的に使用する場合、1つのキャビティのアセンブリが他の多数の領域のスキャンに沿って移動し、顕著な休止が発生する可能性があります。

最近、私はよくAzul GCに関する投稿に出会います。AzulGCは、オブジェクトのトポロジ、サイズ、メモリ領域に関係なく、停止することなく動作します。 非常に有望に思えますが、アルゴリズムは特別なLVB命令(ロードされた値のバリア)を必要とするため、彼らのソリューションは独自のハードウェア(AzulのVegaシステム)でしか利用できません。 幸いなことに、Intelプロセッサのx86-64アーキテクチャに同様のメカニズムを実装する機会がようやくあります。 超低遅延アプリケーションをゼロから作成する場合、このJVMの使用を検討しますが、アプリケーションが既に本番環境にあり、その安定性が最も重要な要件の1つである場合は、Oracle HotSpot JVMから任意の別のかなり危険な動き。 5番目から6番目のJavaに移行した場合でも、ユーザーが遭遇した問題の数を思い出してみましょう。

トピックへのリンク:


  1. 公式Javaメモリ管理ドキュメント
  2. ガベージコレクターのセットアップに関するホワイトペーパー
  3. GCに取り組んでいるサンの従業員のブログ。 ここでは、ガベージコレクションのさまざまな側面について詳しく説明します。 強くお勧めします。
  4. JVMメモリ管理のさまざまな側面に関するFAQ
  5. 代替Azul GCの説明。 また、既存のソリューションの欠点を指摘し、その原因を説明します。

Source: https://habr.com/ru/post/J116578/


All Articles