JVM で CMS GC が発生する状況は 5 つありますが、そのすべてを知っている人はおそらくいないでしょう。

JVM で CMS GC が発生する状況は 5 つありますが、そのすべてを知っている人はおそらくいないでしょう。

学生から、アプリケーション内の Old Gen の使用率が CMSInitiatingOccupancyFraction パラメータで設定されたしきい値に達していないのに、なぜ CMS GC がトリガーされるのかとよく質問されます。彼らは、それは非常に奇妙であり、どこに問題があるのか​​わからないと言います。

[[267920]]

実際、CMS GC には、CMSInitiatingOccupancyFraction しきい値トリガーだけでなく、多くのトリガー条件があります。この記事では、ソース コードを通じて CMS GC をトリガーする条件を包括的に整理し、日常生活で遭遇する奇妙な CMS GC の問題を理解できるようにします。

皆さんの注意を引くために、まずいくつか質問をさせてください。

  • Old Gen の使用率が 50% しかないのに、なぜ CMS GC が実行されるのでしょうか?
  • Metaspace の使用によって CMS GC もトリガーされますか?
  • Old Gen の使用率が非常に小さいのに CMS GC が実行される理由は何ですか?

トリガー条件

CMS GC は実装上、フォアグラウンド コレクターとバックグラウンド コレクターに分かれています。フォアグラウンド コレクターは比較的単純ですが、バックグラウンド コレクターはより複雑で、状況も多くなります。

次に、フォアグラウンド コレクターとバックグラウンド コレクターのトリガー条件をそれぞれ説明します。

注: この記事はJDK 8に基づいています

注: この記事では、CMS GC のトリガー条件についてのみ説明します。アルゴリズムの具体的なプロセスと、MSC (マーク スイープ コンパクション) をいつ実行するかは、この記事の範囲外です。

フォアグラウンドコレクター

フォアグラウンドコレクターのトリガー条件は比較的単純です。通常、オブジェクトが割り当てられているが十分なスペースがない場合は、GC が直接トリガーされ、すぐにスペースが再利用されます。使用されるアルゴリズムは、圧縮なしのマークスイープです。

背景コレクター

バックグラウンド コレクターのトリガー条件を説明する前に、まずバックグラウンド コレクターのプロセスについて説明します。 CMS バックグラウンド スレッドを継続的にスキャンします。主なプロセスは、バックグラウンド コレクターのトリガー条件が満たされているかどうかを判断することです。条件が満たされると、バックグラウンド収集が実行されます。

  1. void ConcurrentMarkSweepThread::run() {
  2. ...//省略
  3. 終了する必要がありますが、
  4. 次のサイクルの前にスリープします。
  5. if (_should_terminate) break;
  6. GCCause::Cause 原因 = _collector->_full_gc_requested?
  7. _collector->_full_gc_cause : GCCause::_cms_concurrent_mark;
  8. _collector->collect_in_background( false 、原因);
  9. }
  10. ...//省略
  11. }

各スキャン中、まず CMSWaitDuration 時間待機し、次に shouldConcurrentCollect 判定を実行して、CMS バックグラウンド コレクターのトリガー条件が満たされているかどうかを確認します。 CMSWaitDuration のデフォルト時間は 2 秒です (ビジネスでは CMS GC が頻繁に発生します。各 CMS GC 間の時間間隔に注意してください。2 秒の場合、基本的に CMS のバックグラウンド コレクターであると判断できます)。

  1. void ConcurrentMarkSweepThread::sleepBeforeNextCycle() {
  2. (!_終了する必要があります){
  3. (CMS増分モード)の場合{
  4. icms_wait();
  5. CMSWaitDuration >= 0 の場合
  6. //次の同期GC、つまり同時フルGCまで待機します
  7. // リクエストまたはタイムアウトのいずれか早い
  8. 清掃のためにcms_lock_on_scavengeを待機します(CMSWaitDuration);
  9. }
  10. 戻る;
  11. }それ以外{
  12. CMSWaitDuration >= 0 の場合
  13. //次の同期GC、つまり同時フルGCまで待機します
  14. // リクエストまたはタイムアウトのいずれか早い
  15. ロック解除のために CMS ロックを待機します。
  16. }それ以外{
  17. // cms_lockイベント発生するか、  チェック間隔  shouldConcurrentCollectを永続的に呼び出す
  18. wait_on_cms_lock(CMSチェック間隔);
  19. }
  20. }
  21. // CMS 収集サイクルを開始する必要があるかどうかを確認します
  22. _collector->shouldConcurrentCollect() の場合 {
  23. 戻る;
  24. }
  25. // .. コレクション基準がまだ満たされていないので、戻りましょう
  26. //そしてもう少し待つ
  27. }
  28. }

では、shouldConcurrentCollect() メソッドの条件は何でしょうか?

  1. ブールCMSCollector::shouldConcurrentCollect() {
  2. //*** トリガー状況
  3. _full_gc_requested の場合 {
  4. if (Verbose && PrintGCDetails) {
  5. gclog_or_tty->print_cr( "CMSCollector: 明示的な " のため収集します 
  6. 「 gc リクエスト (または gc_locker)」 );
  7. }
  8. 戻る 真実;
  9. }
  10. // デバッグのために、コレクションタイプを変更します
  11. // 回転 ない 同時収集について
  12. // 型の場合、同時コレクションを開始しないでください。
  13. NOT_PRODUCT(
  14. if (RotateCMSCollectionTypes &&
  15. (_cmsGen->debug_collection_type() !=
  16. コンカレントマークスイープ生成::コンカレントコレクションタイプ)) {
  17. アサート(_cmsGen->debug_collection_type() !=
  18. ConcurrentMarkSweepGeneration::Unknown_collection_type、
  19. 「CMS コレクション タイプが不正です」 );
  20. 戻る 間違い;
  21. }
  22. フリーリストロッカー x(これ);
  23. // -------------------------------------------------------------------------------  
  24. // 開始影響する多く情報を出力します 
  25. // コレクション。
  26. PrintCMSInitiationStatistics が stats().valid() の場合 {
  27. gclog_or_tty->print( "CMSCollector は同時収集する必要があります: " );
  28. gclog_or_tty->スタンプ();
  29. gclog_or_tty->print_cr( "" );
  30. 統計情報()。print_on(gclog_or_tty);
  31. gclog_or_tty->print_cr( "cms_gen_full までの時間 %3.7f" ,
  32. 統計()。cms_gen_full()までの時間。
  33. gclog_or_tty->print_cr( "free=" SIZE_FORMAT, _cmsGen-> free ());
  34. gclog_or_tty->print_cr( "連続利用可能=" SIZE_FORMAT,
  35. _cmsGen->連続利用可能());
  36. gclog_or_tty->print_cr( "promotion_rate=%g" , stats().promotion_rate());
  37. gclog_or_tty->print_cr( "cms_allocation_rate=%g" 、 stats().cms_allocation_rate());
  38. gclog_or_tty->print_cr( "占有率=%3.7f" 、 _cmsGen->占有率());
  39. gclog_or_tty->print_cr( "initiatingOccupancy=%3.7f" 、 _cmsGen->initiating_occupancy());
  40. gclog_or_tty->print_cr( "メタデータが%dに初期化されました" ,
  41. MetaspaceGC::should_concurrent_collect() を使用します。
  42. }
  43. // -------------------------------------------------------------------------------  
  44. // 2番目のトリガー状況
  45. // 推定時間  CMS コレクションを完了する (cms_duration() )
  46. // CMS生成までの推定残り時間より短い
  47. // いっぱいになったら、コレクションを開始します。
  48. if (!UseCMSInitiatingOccupancyOnly) {
  49. (統計()が有効()の場合){
  50. (統計().cms_startまでの時間() == 0.0)の場合){
  51. 戻る 真実;
  52. }
  53. }それ以外{
  54. //やや早め保守的収集したい 注文 
  55. //試しみて  CMS/プロモーション統計を「ブートストラップ」します
  56. // このブランチは最初CMSが成功した後は実行されません
  57. // コレクションを無効にてください。統計が有効になるはずです。
  58. _cmsGen->occupancy() >= _bootstrap_occupancy の場合 {
  59. if (Verbose && PrintGCDetails) {
  60. gclog_or_tty->print_cr(
  61. 「CMSCollector: ブートストラップ統計を収集します:」  
  62. " 占有率 = %f、ブート占有率 = %f" 、_cmsGen->occupancy()、
  63. _bootstrap_occupancy);
  64. }
  65. 戻る 真実;
  66. }
  67. }
  68. }
  69. // 3番目のトリガー状況
  70. // それ以外の場合は、収集サイクルを開始します。
  71. // 古い世代では、収集サイクルを開始する必要があります。それぞれが使用できる
  72. //この決定を行うための適切な基準。
  73. // XXX genの拡張
  74. // 基準はこれよく一致します。 XXXこれを修正する必要あります
  75. _cmsGen->should_concurrent_collect() の場合 {
  76. if (Verbose && PrintGCDetails) {
  77. gclog_or_tty->print_cr( "CMS 古い世代が開始されました" );
  78. }
  79. 戻る 真実;
  80. }
  81. // 4番目のトリガー状況
  82. // 増分コレクションが失敗する可能性があると判断した場合はコレクションを開始します。
  83. // これ 実際にはあまり生産的ないだろう。
  84. // とにかく遅い。
  85. GenCollectedHeap* gch = GenCollectedHeap::heap();
  86. アサート(gch->collector_policy()->is_two_generation_policy()、
  87. 「以下の内容が正しいかどうか確認することをお勧めします」 );
  88. if (gch->incremental_collection_will_fail( true /* consult_young */)) {
  89. if (Verbose && PrintGCDetails) {
  90. gclog_or_tty->print( "CMSCollector: 増分コレクションが失敗するため、収集してください " );
  91. }
  92. 戻る 真実;
  93. }
  94. // 5番目のトリガー状況
  95. (MetaspaceGC::should_concurrent_collect())の場合{
  96. if (Verbose && PrintGCDetails) {
  97. gclog_or_tty->print( "CMSCollector: メタデータ割り当てのために収集します " );
  98. }
  99. 戻る 真実;
  100. }
  101. 戻る 間違い;
  102. }

上記のコードから、バックグラウンド コレクターには 5 つのトリガー状況があることがわかります。

1. 並列フルGCですか?

つまり、GC の原因が gclocker で GCLockerInvokesConcurrent パラメータが設定されている場合、または GC の原因が javalangsystemgc (つまり、System.gc() 呼び出し) で ExplicitGCInvokesConcurrent パラメータが設定されている場合、バックグラウンド コレクターが 1 回トリガーされます。

2. 統計データに基づく動的計算 (UseCMSInitiatingOccupancyOnly が構成されていない場合のみ) UseCMSInitiatingOccupancyOnly が構成されていない場合、CMS GC が必要かどうかは統計データに基づいて動的に決定されます。

判断ロジックは、予測された CMS GC が完了するのに必要な時間が、古い世代がいっぱいになるまでにかかる推定時間よりも長い場合に、GC が実行されるというものです。これらの判断は、過去の CMS GC 統計指標に基づいて行う必要があります。ただし、最初の CMS GC では、統計データはまだ形成されておらず、無効です。このとき、Old Gen の使用率によって GC を実行するかどうかが決定されます。

  1. if (!UseCMSInitiatingOccupancyOnly) {
  2. (統計()が有効()の場合){
  3. (統計().cms_startまでの時間() == 0.0)の場合){
  4. 戻る 真実;
  5. }
  6. }それ以外{
  7. //やや早め保守的収集したい 注文 
  8. //試しみて  CMS/プロモーション統計を「ブートストラップ」します
  9. // このブランチは最初CMSが成功した後は実行されません
  10. // コレクションを無効にてください。統計が有効になるはずです。
  11. _cmsGen->occupancy() >= _bootstrap_occupancy の場合 {
  12. if (Verbose && PrintGCDetails) {
  13. gclog_or_tty->print_cr(
  14. 「CMSCollector: ブートストラップ統計を収集します:」  
  15. " 占有率 = %f、ブート占有率 = %f" 、_cmsGen->occupancy()、
  16. _bootstrap_occupancy);
  17. }
  18. 戻る 真実;
  19. }
  20. }
  21. }

リサイクルは何パーセントから始まりますか? (つまり、bootstrapoccupancy の値はいくらでしょうか?) 答えは 50% です。おそらくあなたも同じようなケースに遭遇したことがあるでしょう。 UseCMSInitiatingOccupancyOnly が構成されていない場合、古い世代が 50% を占めたときに CMS GC が実行されていることがわかりました。その時、あなたは混乱したかもしれません。

  1. _bootstrap_occupancy = (( double )CMSBootstrapOccupancy)/( double )100;
  2. //パラメータのデフォルト値
  3. 製品(uintx, CMSBootstrapOccupancy, 50,
  4. 「ブートストラップ収集統計の CMS 収集を開始する CMS 生成占有率」 )

3. 老将の状況から判断する

  1. bool ConcurrentMarkSweepGeneration::should_concurrent_collect() 定数 {
  2. ロックをアサートする
  3. 占有率() > 開始占有率() の場合 {
  4. if (PrintGCDetails && Verbose) {
  5. gclog_or_tty->print( " %s: 占有率 %f / %f のため収集 " ,
  6. short_name()、occupancy()、initiating_occupancy());
  7. }
  8. 戻る 真実;
  9. }
  10. if (CMSInitiatingOccupancyOnly を使用する) {
  11. 戻る 間違い;
  12. }
  13. (expansion_cause() == CMSExpansionCause::_satisfy_allocation)の場合{
  14. if (PrintGCDetails && Verbose) {
  15. gclog_or_tty->print( " %s: 割り当てのために拡張されたため収集します " ,
  16. ショートネーム());
  17. }
  18. 戻る 真実;
  19. }
  20. _cmsSpace->should_concurrent_collect() の場合 {
  21. if (PrintGCDetails && Verbose) {
  22. gclog_or_tty->print( " %s: cmsSpace の指示に従って収集します " ,
  23. ショートネーム());
  24. }
  25. 戻る 真実;
  26. }
  27. 戻る 間違い;
  28. }

ソース コードからは、2 つの主なカテゴリがわかります。(a) Old Gen のスペース使用率がしきい値と比較されます。しきい値より大きい場合、CMS GC が実行されます (つまり、「occupancy() > initiatingoccupancy()」)。占有率は間違いなく Old Gen スペースの現在の使用率ですが、占有率の開始とは何でしょうか?

  1. _cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction、CMSTriggerRatio);
  2. ...
  3. void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) {
  4. assert(io <= 100 && tr <= 100, "引数を確認してください" );
  5. io >= 0 の場合
  6. _initiating_occupancy = ( double )io / 100.0;
  7. }それ以外{
  8. _initiating_occupancy = ((100 - MinHeapFreeRatio) +
  9. ( double )(tr *MinHeapFreeRatio) / 100.0)
  10. / 100.0;
  11. }
  12. }

CMSInitiatingOccupancyFraction パラメータの設定値が 0 より大きい場合は、「io / 100.0」であることがわかります。

CMSInitiatingOccupancyFraction パラメータ構成値が 0 未満の場合 (デフォルトは -1 であることに注意してください)、値は "((100 - MinHeapFreeRatio) + (double)(tr * MinHeapFreeRatio) / 100.0) / 100.0" になります。これは何ですか? 92%です。具体的な計算過程についてはここでは掲載しません。おそらく、CMSInitiatingOccupancyFraction が構成されていない場合は 92 であると、何らかの書籍やブログで学んだことがあるでしょう。しかし、実際には、CMSInitiatingOccupancyFraction が構成されていない場合は -1 であるため、しきい値は後者の 92% であり、CMSInitiatingOccupancyFraction の値が 92 であるわけではありません。

(b) 次に、UseCMSInitiatingOccupancyOnlyが設定されていない場合

ここには 2 つのサブカテゴリがあります。

  • オブジェクトの割り当てにより Old Gen が拡張され、スペースが正常に割り当てられると、CMS GC が考慮されます。
  • CMS Gen アイドル チェーンから判断すると、これは少し複雑で、まだ解明されていません。幸いなことに、デフォルトの構成によれば、ここでは実際には false が返されるため、このトリガー条件をデフォルトで考慮する必要はありません。

4. 増分GCが失敗するかどうかに基づく(悲観的な戦略)

それはどういう意味ですか? 2世代GCシステムでは、主に若いGCが失敗するかどうかを指します。 Young GC が失敗したか失敗する可能性がある場合、JVM は CMS GC が必要であると判断する。

  1. ブール増分コレクションが失敗する(ブールconsult_young) {
  2. // 2世代システムを想定しています。最初の分離項は、
  3. // 増分コレクションは失敗しました。 ( 2 番目の論理)
  4. // そうならないだろう
  5. アサート(ヒープ()->コレクターポリシー()->2世代ポリシー()、
  6. 「次の定義はn(>2)世代システムには適さない可能性があります」 );
  7. incremental_collection_failed()を返します||
  8. (consult_young && !get_gen(0)->collection_attempt_is_safe());
  9. }

2つの判定条件「incrementalcollectionfailed()」と「!getgen(0)->collectionattemptissafe()」を見てみましょう。ここでのincrementalcollectionfailed()はYoung GCが失敗したことを意味します。失敗する理由としては、通常、Old Gen に昇格したオブジェクトを収容するのに十分なスペースがないことが挙げられます。

!getgen(0)->collectionattemptissafe() は、新しい世代の昇格が安全かどうかを参照します。現在の Old Gen の残りのスペースが Young GC によって昇格されたオブジェクトのサイズを収容するのに十分であるかどうかを判断します。 Young GC がどの程度昇格するかを事前に知ることは不可能であるため、ここでは、各 Young GC 昇格の平均サイズと、現在の Young GC が昇格する可能性のある最大サイズを比較します。

  1. //av_promo は各 YoungGC プロモーションの平均サイズ、max_promotion_in_bytes は現在可能な最大プロモーション サイズ (eden +現在使用されているスペースのサイズ)です
  2. bool res = (利用可能 >= av_promo) || (利用可能 >= max_promotion_in_bytes);

5. メタ空間の状況に基づいて判断する

ここでは、メタスペースが拡張される前に CMSClassUnloadingEnabled パラメータが構成されている場合に設定される、メタスペースの shouldconcurrent_collect フラグを主に確認します。この場合、CMS GC が実行されます。そのため、アプリケーションの起動直後に CMS GC が実行され、Old Gen 領域がわずかな割合を占めるという状況がよく発生し、非常に困惑することになります。実はこれが理由です。

要約する

この記事では、CMS GC のフォアグラウンド コレクターとバックグラウンド コレクターのトリガー条件を整理します。フォアグラウンド コレクターのトリガー条件は比較的単純ですが、バックグラウンド コレクターのトリガー条件はより多く、5 つの主要な状況に分けることができます。それぞれの大きな状況には、いくつかの小さなトリガー分岐もあります。特に、UseCMSInitiatingOccupancyOnly パラメータが構成されていない場合は、トリガーの可能性がさらに多くなります。一般的に、CMS GC が確実に実行されるように、実稼働環境で UseCMSInitiatingOccupancyOnly パラメータを構成することを強くお勧めします。また、GC の原因をトラブルシューティングするのにも便利です。

<<:  AWSはアマゾンから分離される可能性、CEOは合意に従うと語る

>>:  Microsoft Azure SQL Database Managed Instance は、SQL Server から PaaS サービスへのシームレスな移行の時代を切り開きます。

推薦する

コンテナオペレーターが知っておくべき Kubernetes (K8s) クラスターの 10 個の一般的な API リソースオブジェクト

Kubernetes (略して K8s) は、コンテナ化されたアプリケーションの展開、スケーリング、...

#BlackFriday# Contabo: 月額 8.49 ドル、シンガポール/米国/ドイツ/英国、8G メモリ/4 コア (AMD EPYC)/200gSSD/32T トラフィック、Windows をサポート

contabo のブラックフライデーをご紹介します。VPS、VDS、専用サーバー、ブロックストレージ...

モバイル検索は独立して発展するべきでしょうか、それとも二次的な機能になるべきでしょうか?

誰もが話題の合併や買収、製品のアップグレードについて話しているとき、かつてのデスクトップの覇者であっ...

dedipath: (複数の) 専用サーバーのクリアランス、ロサンゼルス、1Gbps 無制限トラフィック、20G 防御、月額 55 ドルから (e3-1240v2/16g/2T/5IP)

ロサンゼルスのデータセンターにある Dedipath の独立サーバーが一掃されつつあり、損益分岐点の...

リンクを含むブログ記事を取得するためのいくつかの重要なポイント

グループ内で「私のブログは一度も掲載されません。リンクがなくてもすぐに掲載されるのに、リンクが張られ...

zipvps-1g メモリ kvm/50g ハードディスク/1T トラフィック/G ポート/ニューヨーク/月額 6.5 ドル

zipvps はいくつかのプロモーションを行ってきました。ワンマン商人ホスト猫として、これまでここで...

Duomao Interactiveはインターネットの光を照らし、企業の効率的なマーケティングを支援します

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています今日、イン...

ウェブサイトはコンテンツソースの問題をどのように解決すべきでしょうか?

インターネットには今や膨大な量のコンテンツが溢れています。多くのウェブマスターは、ウェブサイトのコン...

A5ウェブマスターがインターネット体験の足がかりを得る方法についての簡単な説明

第7回中国インターネットウェブマスター年次大会が終了し、A5ウェブマスターの間でも危機感が高まってい...

無視できないWebサイト構築計画。4つの主要計画ポイントを詳しく解説

今、すべてがスピードアップしているようです。鉄道はスピードアップする必要があり、CPIもスピードアッ...

chicagovps-$49/AtomC2750/8g メモリ/250g ハードディスク/10T トラフィック

chicagovps からプロモーションメッセージが届きました。今回は低価格の VPS と低価格の専...

perfectionservers-512m メモリ/1g バースト/10g SSD/9.97 ドル

perfectionservers、HostCat の公式 Web サイトには背景についての紹介はな...

野蛮な時代を脱した電子商取引:資本注入から運用収益性へ移行する方法

自由化の急速な成長を経て、中国の電子商取引はどこへ向かうのでしょうか。昨日の「2012 中国(深圳)...

SaaS アプリケーションからデータをクエリして抽出するにはどうすればよいですか?

[51CTO.com クイック翻訳] 各 SaaS アプリケーションの背後にあるデータベースには、従...