JVMソース解読のCMSはいつFull GCを行うのか

6131 ワード

上の「洗生のブログ」をクリックして、私に注目してください。


転載はオリジナルの出所を明記してください.ありがとうございます.読み終わって収穫があると思ったら、いいねと注目してください.

前言


本稿の内容はJDK 8に基づく
文章JVMソースコード解読のCMS GCトリガ条件でCMS GCトリガの5種類を分析し,CMS GCはforeground collectorとbackground collectorに分けられると述べた.foreground collectorでもbackground collectorでもmark-sweepアルゴリズムを使用し、段階的にタグクリーンアップを行う利点は明らか-低遅延であるが、最大の欠点はフラグメントが存在し、メモリスペースの利用率が低いことである.そこで、CMSはこの問題を解決するためにforeground collectorを行うたびに、圧縮式GCを1回行う必要があるか否かを判断する.
この圧縮式GCでは、CMSはSerial Old GCと同様のLISP 2アルゴリズムを使用し、mark-compactを使用してFull GCを行い、一般的にMSC(mark-sweep-compact)と呼ばれ、JavaスタックのYoung GenとOld Genおよびmetaspace(メタ空間)を収集する.
本稿では,具体的な収集過程には触れず,CMSがどのような場合にcompactを行うかのFull GCのみを分析する.
どのような場合に一度圧縮式フルGCが行われるのでしょうか?

FullGCはいつ行われますか?


次のコードは、CMSがmark-sweepのforeground collectorを行うか、mark-sweep-compactのFull GCを行うかを判断するコードです.主な判断根拠は圧縮するかどうか、すなわちコード中のshould_compact.
// Check if we need to do a compaction, or if not, whether	
// we need to start the mark-sweep from scratch.	
bool should_compact    = false;	
bool should_start_over = false;	
decide_foreground_collection_type(clear_all_soft_refs,	
    &should_compact, &should_start_over);	
...	
if (should_compact) {	
    ...	
    // mark-sweep-compact	
    do_compaction_work(clear_all_soft_refs);	
    ...	
} else {	
    // mark-sweep	
    do_mark_sweep_work(clear_all_soft_refs, first_state,	
      should_start_over);	
}

次に、どのような状況でcompactが行われるかを分析し、decideforegroundcollection_を見てみましょう.type関数は、主に4つのケースに分けられます.
  • GC(foreground collectorとcompactのFull GCを含む)回数
  • GCCauseがユーザ要求式トリガによる
  • であるかどうか
  • 増分GCが失敗する可能性があるかどうか(悲観的な戦略)
  • すべてのSoftReference
  • をクリーンアップするかどうか
    void CMSCollector::decide_foreground_collection_type(	
      bool clear_all_soft_refs, bool* should_compact,	
      bool* should_start_over) {	
      ...	
      //  	
      *should_compact =	
        UseCMSCompactAtFullCollection &&	
        ((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction) ||	
         GCCause::is_user_requested_gc(gch->gc_cause()) ||	
         gch->incremental_collection_will_fail(true /* consult_young */));	
      *should_start_over = false;	
      if (clear_all_soft_refs && !*should_compact) {	
        if (CMSCompactWhenClearAllSoftRefs) {	
          *should_compact = true;	
        } else {	
            if (_collectorState > FinalMarking) {	
            _collectorState = Resetting; // skip to reset to start new cycle	
            reset(false /* == !asynch */);	
            *should_start_over = true;	
          } 	
        }	
      }	
    }

    次に、それぞれの状況を具体的に見てみましょう.

    1.GC(foreground collectorとcompactのFull GCを含む)回数

    // UseCMSCompactAtFullCollection   true	
    UseCMSCompactAtFullCollection &&	
        ((_full_gcs_since_conc_gc >= CMSFullGCsBeforeCompaction)

    ここで言うGC回数fullgcssinceconc_gcとは、前回background collector以降、foreground collectorとcompactのFull GCの回数であり、CMSFullGCsBeforeCompactionパラメータ閾値以上であれば、一次圧縮式のFull GCが可能であることを示す.(CMSFullGCsBeforeCompactionパラメータのデフォルトは0であり、デフォルトは圧縮式のFull GCであることを意味する)

    2.GCCauseがユーザ要求式トリガによるものか

     inline static bool is_user_requested_gc(GCCause::Cause cause) {	
        return (cause == GCCause::_java_lang_system_gc ||	
                cause == GCCause::_jvmti_force_gc);	
      }

    ユーザ要求式トリガによるGCCauseとはjavalangsystemgc(すなわちSystem.gc()またはjvmtiforce_を指すgc(すなわちJVMTi方式の強制GC)は、Systemである限りであることを意味する.gc(ExplicitGInvokesConcurrentパラメータが構成されていないことを前提としている)呼び出しまたはJVMTi方式の強制GCでは、いずれも圧縮式のFull GCが行われます.

    3.増分GCが失敗する可能性があるかどうか(悲観策)

      bool incremental_collection_will_fail(bool consult_young) {	
        // Assumes a 2-generation system; the first disjunct remembers if an	
        // incremental collection failed, even when we thought (second disjunct)	
        // that it would not.	
        assert(heap()->collector_policy()->is_two_generation_policy(),	
               "the following definition may not be suitable for an n(>2)-generation system");	
        return incremental_collection_failed() ||	
               (consult_young && !get_gen(0)->collection_attempt_is_safe());	
      }

    JVMソース解読のCMS GCトリガ条件記事でもこの内容に言及していますが、2世代のGCシステムのうち、主にYoung GCが失敗するかどうかを指しています.Young GCが失敗したり失敗したりした場合、CMSは破片による可能性があると判断し、圧縮式のFull GCを1回行う必要がある.
    「incrementalcollectionfailed()」とは、Young GCが失敗したことを意味し、なぜ失敗したのかは、一般的にOld Genが昇進対象を収容するのに十分な空間がないためであり、例えば一般的な「promotion failed」である.
    「!getgen(0)->collectionattemptissafe()」とは、Young Genの生存オブジェクトの昇進が失敗する可能性があるかどうかを意味します.現在のOld Genの残りの空間サイズがYoung GC昇進の対象サイズを十分に収容できるかどうかを判断する.Young GCがどれだけ昇進するかは事前に知ることができないので、ここではYoung GCごとの昇進の平均の大きさと現在のYoung GCが昇進する可能性のある最大の大きさを統計することで比較します.
    次に示すのはcollectionattemptisです.safe関数のコード:
    bool DefNewGeneration::collection_attempt_is_safe() {	
      if (!to()->is_empty()) {	
        if (Verbose && PrintGCDetails) {	
          gclog_or_tty->print(" :: to is not empty :: ");	
        }	
        return false;	
      }	
      if (_next_gen == NULL) {	
        GenCollectedHeap* gch = GenCollectedHeap::heap();	
        _next_gen = gch->next_gen(this);	
      }	
      return _next_gen->promotion_attempt_is_safe(used());	
    }

    4.すべてのSoftReferenceをクリーンアップするかどうか

    if (clear_all_soft_refs && !*should_compact) {	
        if (CMSCompactWhenClearAllSoftRefs) {	
          *should_compact = true;	
        } 	
        ...

    SoftReferenceソフトリファレンスは、メモリが足りない場合、GCが関連オブジェクトのメモリを回収するのが一般的であることを理解する必要があります.ここではすべてのソフトリファレンスを回収する必要がある場合、CMSCompactWhenClearAllSoftRefsパラメータが設定されている場合、一度圧縮式のFull GCが行われます.
    JDK 1.9は、CMS forground collectorの機能を徹底的に排除した、つまりbackground collectorのほか、圧縮式のFull GCという変更があります.ナチュラル(U s e CMSCompactAtFullCollection、CMSFullGCsBeforeCompactionの2つのパラメータもサポートされていません.

    まとめ


    本稿では、CMSが以下の4つのケースについて重点的に紹介した.
  • GC(foreground collectorとcompactのFull GCを含む)回数
  • GCCauseがユーザ要求式トリガであるかどうかにより
  • が生じる.
  • 増分GCが失敗する可能性があるかどうか(悲観的な戦略)
  • すべてのSoftReference
  • をクリーンアップするかどうか
    圧縮式のフルGCが行われ,それぞれの場合のトリガ条件について詳細に説明した.我々はGCチューニング時に圧縮式のFull GCをできるだけ避けるべきである.Serial Old GC類似アルゴリズムを使用しているため、単一スレッドで全スタックおよびmetaspaceを回収し、STWの時間が特に長く、ビジネスシステムの可用性に大きな影響を及ぼす.