0%

# JVM源码分析之G1垃圾收集器分析一

Posted on 2019-12-07 Edited on 2019-12-20 In Java , JVM , C++ , GC Views: Views: Disqus:

对 Java 的 gc 实现比较感兴趣，原先一般都是看周志明的书，但其实并没有讲具体的 gc 源码，而是把整个思路和流程讲解了一下
特别是 G1 的具体实现
一般对 G1 的理解其实就是把原先整块的新生代老年代分成了以 region 为单位的小块内存，简而言之，就是原先对新生代老年代的收集会涉及到整个代的堆内存空间，而G1 把它变成了更细致的小块内存
这带来了一个很明显的好处和一个很明显的坏处，好处是内存收集可以更灵活，耗时会变短，但整个收集的处理复杂度就变高了
目前看了一点点关于 G1 收集的预期时间相关的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size,
                                                   uint gc_count_before,
                                                   bool* succeeded,
                                                   GCCause::Cause gc_cause) {
      assert_heap_not_locked_and_not_at_safepoint();
      VM_G1CollectForAllocation op(word_size,
                                   gc_count_before,
                                   gc_cause,
                                   false, /* should_initiate_conc_mark */
                                   g1_policy()->max_pause_time_ms());
      VMThread::execute(&op);

      HeapWord* result = op.result();
      bool ret_succeeded = op.prologue_succeeded() && op.pause_succeeded();
      assert(result == NULL || ret_succeeded,
             "the result should be NULL if the VM did not succeed");
      *succeeded = ret_succeeded;

      assert_heap_not_locked();
      return result;
    }

这里就是收集时需要停顿的，其中`VMThread::execute(&op);`是具体执行的，真正执行的是`VM_G1CollectForAllocation::doit`方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85

    void VM_G1CollectForAllocation::doit() {
      G1CollectedHeap* g1h = G1CollectedHeap::heap();
      assert(!_should_initiate_conc_mark || g1h->should_do_concurrent_full_gc(_gc_cause),
          "only a GC locker, a System.gc(), stats update, whitebox, or a hum allocation induced GC should start a cycle");

      if (_word_size > 0) {
        // An allocation has been requested. So, try to do that first.
        _result = g1h->attempt_allocation_at_safepoint(_word_size,
                                                       false /* expect_null_cur_alloc_region */);
        if (_result != NULL) {
          // If we can successfully allocate before we actually do the
          // pause then we will consider this pause successful.
          _pause_succeeded = true;
          return;
        }
      }

      GCCauseSetter x(g1h, _gc_cause);
      if (_should_initiate_conc_mark) {
        // It's safer to read old_marking_cycles_completed() here, given
        // that noone else will be updating it concurrently. Since we'll
        // only need it if we're initiating a marking cycle, no point in
        // setting it earlier.
        _old_marking_cycles_completed_before = g1h->old_marking_cycles_completed();

        // At this point we are supposed to start a concurrent cycle. We
        // will do so if one is not already in progress.
        bool res = g1h->g1_policy()->force_initial_mark_if_outside_cycle(_gc_cause);

        // The above routine returns true if we were able to force the
        // next GC pause to be an initial mark; it returns false if a
        // marking cycle is already in progress.
        //
        // If a marking cycle is already in progress just return and skip the
        // pause below - if the reason for requesting this initial mark pause
        // was due to a System.gc() then the requesting thread should block in
        // doit_epilogue() until the marking cycle is complete.
        //
        // If this initial mark pause was requested as part of a humongous
        // allocation then we know that the marking cycle must just have
        // been started by another thread (possibly also allocating a humongous
        // object) as there was no active marking cycle when the requesting
        // thread checked before calling collect() in
        // attempt_allocation_humongous(). Retrying the GC, in this case,
        // will cause the requesting thread to spin inside collect() until the
        // just started marking cycle is complete - which may be a while. So
        // we do NOT retry the GC.
        if (!res) {
          assert(_word_size == 0, "Concurrent Full GC/Humongous Object IM shouldn't be allocating");
          if (_gc_cause != GCCause::_g1_humongous_allocation) {
            _should_retry_gc = true;
          }
          return;
        }
      }

      // Try a partial collection of some kind.
      _pause_succeeded = g1h->do_collection_pause_at_safepoint(_target_pause_time_ms);

      if (_pause_succeeded) {
        if (_word_size > 0) {
          // An allocation had been requested. Do it, eventually trying a stronger
          // kind of GC.
          _result = g1h->satisfy_failed_allocation(_word_size, &_pause_succeeded);
        } else {
          bool should_upgrade_to_full = !g1h->should_do_concurrent_full_gc(_gc_cause) &&
                                        !g1h->has_regions_left_for_allocation();
          if (should_upgrade_to_full) {
            // There has been a request to perform a GC to free some space. We have no
            // information on how much memory has been asked for. In case there are
            // absolutely no regions left to allocate into, do a maximally compacting full GC.
            log_info(gc, ergo)("Attempting maximally compacting collection");
            _pause_succeeded = g1h->do_full_collection(false, /* explicit gc */
                                                       true   /* clear_all_soft_refs */);
          }
        }
        guarantee(_pause_succeeded, "Elevated collections during the safepoint must always succeed.");
      } else {
        assert(_result == NULL, "invariant");
        // The only reason for the pause to not be successful is that, the GC locker is
        // active (or has become active since the prologue was executed). In this case
        // we should retry the pause after waiting for the GC locker to become inactive.
        _should_retry_gc = true;
      }
    }

这里可以看到核心的是`G1CollectedHeap::do_collection_pause_at_safepoint`这个方法，它带上了目标暂停时间的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330

    G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
      assert_at_safepoint_on_vm_thread();
      guarantee(!is_gc_active(), "collection is not reentrant");

      if (GCLocker::check_active_before_gc()) {
        return false;
      }

      _gc_timer_stw->register_gc_start();

      GCIdMark gc_id_mark;
      _gc_tracer_stw->report_gc_start(gc_cause(), _gc_timer_stw->gc_start());

      SvcGCMarker sgcm(SvcGCMarker::MINOR);
      ResourceMark rm;

      g1_policy()->note_gc_start();

      wait_for_root_region_scanning();

      print_heap_before_gc();
      print_heap_regions();
      trace_heap_before_gc(_gc_tracer_stw);

      _verifier->verify_region_sets_optional();
      _verifier->verify_dirty_young_regions();

      // We should not be doing initial mark unless the conc mark thread is running
      if (!_cm_thread->should_terminate()) {
        // This call will decide whether this pause is an initial-mark
        // pause. If it is, in_initial_mark_gc() will return true
        // for the duration of this pause.
        g1_policy()->decide_on_conc_mark_initiation();
      }

      // We do not allow initial-mark to be piggy-backed on a mixed GC.
      assert(!collector_state()->in_initial_mark_gc() ||
              collector_state()->in_young_only_phase(), "sanity");

      // We also do not allow mixed GCs during marking.
      assert(!collector_state()->mark_or_rebuild_in_progress() || collector_state()->in_young_only_phase(), "sanity");

      // Record whether this pause is an initial mark. When the current
      // thread has completed its logging output and it's safe to signal
      // the CM thread, the flag's value in the policy has been reset.
      bool should_start_conc_mark = collector_state()->in_initial_mark_gc();

      // Inner scope for scope based logging, timers, and stats collection
      {
        EvacuationInfo evacuation_info;

        if (collector_state()->in_initial_mark_gc()) {
          // We are about to start a marking cycle, so we increment the
          // full collection counter.
          increment_old_marking_cycles_started();
          _cm->gc_tracer_cm()->set_gc_cause(gc_cause());
        }

        _gc_tracer_stw->report_yc_type(collector_state()->yc_type());

        GCTraceCPUTime tcpu;

        G1HeapVerifier::G1VerifyType verify_type;
        FormatBuffer<> gc_string("Pause Young ");
        if (collector_state()->in_initial_mark_gc()) {
          gc_string.append("(Concurrent Start)");
          verify_type = G1HeapVerifier::G1VerifyConcurrentStart;
        } else if (collector_state()->in_young_only_phase()) {
          if (collector_state()->in_young_gc_before_mixed()) {
            gc_string.append("(Prepare Mixed)");
          } else {
            gc_string.append("(Normal)");
          }
          verify_type = G1HeapVerifier::G1VerifyYoungNormal;
        } else {
          gc_string.append("(Mixed)");
          verify_type = G1HeapVerifier::G1VerifyMixed;
        }
        GCTraceTime(Info, gc) tm(gc_string, NULL, gc_cause(), true);

        uint active_workers = AdaptiveSizePolicy::calc_active_workers(workers()->total_workers(),
                                                                      workers()->active_workers(),
                                                                      Threads::number_of_non_daemon_threads());
        active_workers = workers()->update_active_workers(active_workers);
        log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->total_workers());

        TraceCollectorStats tcs(g1mm()->incremental_collection_counters());
        TraceMemoryManagerStats tms(&_memory_manager, gc_cause(),
                                    collector_state()->yc_type() == Mixed /* allMemoryPoolsAffected */);

        G1HeapTransition heap_transition(this);
        size_t heap_used_bytes_before_gc = used();

        // Don't dynamically change the number of GC threads this early.  A value of
        // 0 is used to indicate serial work.  When parallel work is done,
        // it will be set.

        { // Call to jvmpi::post_class_unload_events must occur outside of active GC
          IsGCActiveMark x;

          gc_prologue(false);

          if (VerifyRememberedSets) {
            log_info(gc, verify)("[Verifying RemSets before GC]");
            VerifyRegionRemSetClosure v_cl;
            heap_region_iterate(&v_cl);
          }

          _verifier->verify_before_gc(verify_type);

          _verifier->check_bitmaps("GC Start");

    #if COMPILER2_OR_JVMCI
          DerivedPointerTable::clear();
    #endif

          // Please see comment in g1CollectedHeap.hpp and
          // G1CollectedHeap::ref_processing_init() to see how
          // reference processing currently works in G1.

          // Enable discovery in the STW reference processor
          _ref_processor_stw->enable_discovery();

          {
            // We want to temporarily turn off discovery by the
            // CM ref processor, if necessary, and turn it back on
            // on again later if we do. Using a scoped
            // NoRefDiscovery object will do this.
            NoRefDiscovery no_cm_discovery(_ref_processor_cm);

            // Forget the current alloc region (we might even choose it to be part
            // of the collection set!).
            _allocator->release_mutator_alloc_region();

            // This timing is only used by the ergonomics to handle our pause target.
            // It is unclear why this should not include the full pause. We will
            // investigate this in CR 7178365.
            //
            // Preserving the old comment here if that helps the investigation:
            //
            // The elapsed time induced by the start time below deliberately elides
            // the possible verification above.
            double sample_start_time_sec = os::elapsedTime();

            g1_policy()->record_collection_pause_start(sample_start_time_sec);

            if (collector_state()->in_initial_mark_gc()) {
              concurrent_mark()->pre_initial_mark();
            }

            g1_policy()->finalize_collection_set(target_pause_time_ms, &_survivor);

            evacuation_info.set_collectionset_regions(collection_set()->region_length());

            // Make sure the remembered sets are up to date. This needs to be
            // done before register_humongous_regions_with_cset(), because the
            // remembered sets are used there to choose eager reclaim candidates.
            // If the remembered sets are not up to date we might miss some
            // entries that need to be handled.
            g1_rem_set()->cleanupHRRS();

            register_humongous_regions_with_cset();

            assert(_verifier->check_cset_fast_test(), "Inconsistency in the InCSetState table.");

            // We call this after finalize_cset() to
            // ensure that the CSet has been finalized.
            _cm->verify_no_cset_oops();

            if (_hr_printer.is_active()) {
              G1PrintCollectionSetClosure cl(&_hr_printer);
              _collection_set.iterate(&cl);
            }

            // Initialize the GC alloc regions.
            _allocator->init_gc_alloc_regions(evacuation_info);

            G1ParScanThreadStateSet per_thread_states(this, workers()->active_workers(), collection_set()->young_region_length());
            pre_evacuate_collection_set();

            // Actually do the work...
            evacuate_collection_set(&per_thread_states);

            post_evacuate_collection_set(evacuation_info, &per_thread_states);

            const size_t* surviving_young_words = per_thread_states.surviving_young_words();
            free_collection_set(&_collection_set, evacuation_info, surviving_young_words);

            eagerly_reclaim_humongous_regions();

            record_obj_copy_mem_stats();
            _survivor_evac_stats.adjust_desired_plab_sz();
            _old_evac_stats.adjust_desired_plab_sz();

            double start = os::elapsedTime();
            start_new_collection_set();
            g1_policy()->phase_times()->record_start_new_cset_time_ms((os::elapsedTime() - start) * 1000.0);

            if (evacuation_failed()) {
              set_used(recalculate_used());
              if (_archive_allocator != NULL) {
                _archive_allocator->clear_used();
              }
              for (uint i = 0; i < ParallelGCThreads; i++) {
                if (_evacuation_failed_info_array[i].has_failed()) {
                  _gc_tracer_stw->report_evacuation_failed(_evacuation_failed_info_array[i]);
                }
              }
            } else {
              // The "used" of the the collection set have already been subtracted
              // when they were freed.  Add in the bytes evacuated.
              increase_used(g1_policy()->bytes_copied_during_gc());
            }

            if (collector_state()->in_initial_mark_gc()) {
              // We have to do this before we notify the CM threads that
              // they can start working to make sure that all the
              // appropriate initialization is done on the CM object.
              concurrent_mark()->post_initial_mark();
              // Note that we don't actually trigger the CM thread at
              // this point. We do that later when we're sure that
              // the current thread has completed its logging output.
            }

            allocate_dummy_regions();

            _allocator->init_mutator_alloc_region();

            {
              size_t expand_bytes = _heap_sizing_policy->expansion_amount();
              if (expand_bytes > 0) {
                size_t bytes_before = capacity();
                // No need for an ergo logging here,
                // expansion_amount() does this when it returns a value > 0.
                double expand_ms;
                if (!expand(expand_bytes, _workers, &expand_ms)) {
                  // We failed to expand the heap. Cannot do anything about it.
                }
                g1_policy()->phase_times()->record_expand_heap_time(expand_ms);
              }
            }

            // We redo the verification but now wrt to the new CSet which
            // has just got initialized after the previous CSet was freed.
            _cm->verify_no_cset_oops();

            // This timing is only used by the ergonomics to handle our pause target.
            // It is unclear why this should not include the full pause. We will
            // investigate this in CR 7178365.
            double sample_end_time_sec = os::elapsedTime();
            double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS;
            size_t total_cards_scanned = g1_policy()->phase_times()->sum_thread_work_items(G1GCPhaseTimes::ScanRS, G1GCPhaseTimes::ScanRSScannedCards);
            g1_policy()->record_collection_pause_end(pause_time_ms, total_cards_scanned, heap_used_bytes_before_gc);

            evacuation_info.set_collectionset_used_before(collection_set()->bytes_used_before());
            evacuation_info.set_bytes_copied(g1_policy()->bytes_copied_during_gc());

            if (VerifyRememberedSets) {
              log_info(gc, verify)("[Verifying RemSets after GC]");
              VerifyRegionRemSetClosure v_cl;
              heap_region_iterate(&v_cl);
            }

            _verifier->verify_after_gc(verify_type);
            _verifier->check_bitmaps("GC End");

            assert(!_ref_processor_stw->discovery_enabled(), "Postcondition");
            _ref_processor_stw->verify_no_references_recorded();

            // CM reference discovery will be re-enabled if necessary.
          }

    #ifdef TRACESPINNING
          ParallelTaskTerminator::print_termination_counts();
    #endif

          gc_epilogue(false);
        }

        // Print the remainder of the GC log output.
        if (evacuation_failed()) {
          log_info(gc)("To-space exhausted");
        }

        g1_policy()->print_phases();
        heap_transition.print();

        // It is not yet to safe to tell the concurrent mark to
        // start as we have some optional output below. We don't want the
        // output from the concurrent mark thread interfering with this
        // logging output either.

        _hrm.verify_optional();
        _verifier->verify_region_sets_optional();

        TASKQUEUE_STATS_ONLY(print_taskqueue_stats());
        TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());

        print_heap_after_gc();
        print_heap_regions();
        trace_heap_after_gc(_gc_tracer_stw);

        // We must call G1MonitoringSupport::update_sizes() in the same scoping level
        // as an active TraceMemoryManagerStats object (i.e. before the destructor for the
        // TraceMemoryManagerStats is called) so that the G1 memory pools are updated
        // before any GC notifications are raised.
        g1mm()->update_sizes();

        _gc_tracer_stw->report_evacuation_info(&evacuation_info);
        _gc_tracer_stw->report_tenuring_threshold(_g1_policy->tenuring_threshold());
        _gc_timer_stw->register_gc_end();
        _gc_tracer_stw->report_gc_end(_gc_timer_stw->gc_end(), _gc_timer_stw->time_partitions());
      }
      // It should now be safe to tell the concurrent mark thread to start
      // without its logging output interfering with the logging output
      // that came from the pause.

      if (should_start_conc_mark) {
        // CAUTION: after the doConcurrentMark() call below,
        // the concurrent marking thread(s) could be running
        // concurrently with us. Make sure that anything after
        // this point does not assume that we are the only GC thread
        // running. Note: of course, the actual marking work will
        // not start until the safepoint itself is released in
        // SuspendibleThreadSet::desynchronize().
        do_concurrent_mark();
      }

      return true;
    }

往下走就是这一步`G1Policy::finalize_collection_set`,去处理新生代和老年代

    1
    2
    3
    4

    void G1Policy::finalize_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) {
      double time_remaining_ms = _collection_set->finalize_young_part(target_pause_time_ms, survivor);
      _collection_set->finalize_old_part(time_remaining_ms);
    }

这里分别调用了两个方法，可以看到剩余时间是往下传的，来看一下具体的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

    double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors) {
      double young_start_time_sec = os::elapsedTime();

      finalize_incremental_building();

      guarantee(target_pause_time_ms > 0.0,
                "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms);

      size_t pending_cards = _policy->pending_cards();
      double base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards);
      double time_remaining_ms = MAX2(target_pause_time_ms - base_time_ms, 0.0);

      log_trace(gc, ergo, cset)("Start choosing CSet. pending cards: " SIZE_FORMAT " predicted base time: %1.2fms remaining time: %1.2fms target pause time: %1.2fms",
                                pending_cards, base_time_ms, time_remaining_ms, target_pause_time_ms);

      // The young list is laid with the survivor regions from the previous
      // pause are appended to the RHS of the young list, i.e.
      //   [Newly Young Regions ++ Survivors from last pause].

      uint survivor_region_length = survivors->length();
      uint eden_region_length = _g1h->eden_regions_count();
      init_region_lengths(eden_region_length, survivor_region_length);

      verify_young_cset_indices();

      // Clear the fields that point to the survivor list - they are all young now.
      survivors->convert_to_eden();

      _bytes_used_before = _inc_bytes_used_before;
      time_remaining_ms = MAX2(time_remaining_ms - _inc_predicted_elapsed_time_ms, 0.0);

      log_trace(gc, ergo, cset)("Add young regions to CSet. eden: %u regions, survivors: %u regions, predicted young region time: %1.2fms, target pause time: %1.2fms",
                                eden_region_length, survivor_region_length, _inc_predicted_elapsed_time_ms, target_pause_time_ms);

      // The number of recorded young regions is the incremental
      // collection set's current size
      set_recorded_rs_lengths(_inc_recorded_rs_lengths);

      double young_end_time_sec = os::elapsedTime();
      phase_times()->record_young_cset_choice_time_ms((young_end_time_sec - young_start_time_sec) * 1000.0);

      return time_remaining_ms;
    }

下面是老年代的部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100

    void G1CollectionSet::finalize_old_part(double time_remaining_ms) {
      double non_young_start_time_sec = os::elapsedTime();
      double predicted_old_time_ms = 0.0;

      if (collector_state()->in_mixed_phase()) {
        cset_chooser()->verify();
        const uint min_old_cset_length = _policy->calc_min_old_cset_length();
        const uint max_old_cset_length = _policy->calc_max_old_cset_length();

        uint expensive_region_num = 0;
        bool check_time_remaining = _policy->adaptive_young_list_length();

        HeapRegion* hr = cset_chooser()->peek();
        while (hr != NULL) {
          if (old_region_length() >= max_old_cset_length) {
            // Added maximum number of old regions to the CSet.
            log_debug(gc, ergo, cset)("Finish adding old regions to CSet (old CSet region num reached max). old %u regions, max %u regions",
                                      old_region_length(), max_old_cset_length);
            break;
          }

          // Stop adding regions if the remaining reclaimable space is
          // not above G1HeapWastePercent.
          size_t reclaimable_bytes = cset_chooser()->remaining_reclaimable_bytes();
          double reclaimable_percent = _policy->reclaimable_bytes_percent(reclaimable_bytes);
          double threshold = (double) G1HeapWastePercent;
          if (reclaimable_percent <= threshold) {
            // We've added enough old regions that the amount of uncollected
            // reclaimable space is at or below the waste threshold. Stop
            // adding old regions to the CSet.
            log_debug(gc, ergo, cset)("Finish adding old regions to CSet (reclaimable percentage not over threshold). "
                                      "old %u regions, max %u regions, reclaimable: " SIZE_FORMAT "B (%1.2f%%) threshold: " UINTX_FORMAT "%%",
                                      old_region_length(), max_old_cset_length, reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
            break;
          }

          double predicted_time_ms = predict_region_elapsed_time_ms(hr);
          if (check_time_remaining) {
            if (predicted_time_ms > time_remaining_ms) {
              // Too expensive for the current CSet.

              if (old_region_length() >= min_old_cset_length) {
                // We have added the minimum number of old regions to the CSet,
                // we are done with this CSet.
                log_debug(gc, ergo, cset)("Finish adding old regions to CSet (predicted time is too high). "
                                          "predicted time: %1.2fms, remaining time: %1.2fms old %u regions, min %u regions",
                                          predicted_time_ms, time_remaining_ms, old_region_length(), min_old_cset_length);
                break;
              }

              // We'll add it anyway given that we haven't reached the
              // minimum number of old regions.
              expensive_region_num += 1;
            }
          } else {
            if (old_region_length() >= min_old_cset_length) {
              // In the non-auto-tuning case, we'll finish adding regions
              // to the CSet if we reach the minimum.

              log_debug(gc, ergo, cset)("Finish adding old regions to CSet (old CSet region num reached min). old %u regions, min %u regions",
                                        old_region_length(), min_old_cset_length);
              break;
            }
          }

          // We will add this region to the CSet.
          time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
          predicted_old_time_ms += predicted_time_ms;
          cset_chooser()->pop(); // already have region via peek()
          _g1h->old_set_remove(hr);
          add_old_region(hr);

          hr = cset_chooser()->peek();
        }
        if (hr == NULL) {
          log_debug(gc, ergo, cset)("Finish adding old regions to CSet (candidate old regions not available)");
        }

        if (expensive_region_num > 0) {
          // We print the information once here at the end, predicated on
          // whether we added any apparently expensive regions or not, to
          // avoid generating output per region.
          log_debug(gc, ergo, cset)("Added expensive regions to CSet (old CSet region num not reached min)."
                                    "old: %u regions, expensive: %u regions, min: %u regions, remaining time: %1.2fms",
                                    old_region_length(), expensive_region_num, min_old_cset_length, time_remaining_ms);
        }

        cset_chooser()->verify();
      }

      stop_incremental_building();

      log_debug(gc, ergo, cset)("Finish choosing CSet. old: %u regions, predicted old region time: %1.2fms, time remaining: %1.2f",
                                old_region_length(), predicted_old_time_ms, time_remaining_ms);

      double non_young_end_time_sec = os::elapsedTime();
      phase_times()->record_non_young_cset_choice_time_ms((non_young_end_time_sec - non_young_start_time_sec) * 1000.0);

      QuickSort::sort(_collection_set_regions, _collection_set_cur_length, compare_region_idx, true);
    }

上面第三行是个判断，当前是否是 mixed 回收阶段，如果不是的话其实是没有老年代什么事的，所以可以看到代码基本是从这个 if 判断
`if (collector_state()->in_mixed_phase()) {`开始往下走的
先写到这，偏向于做笔记用，有错轻拍

Related Posts

- G1收集器概述
- Comparator使用小记
- Maven实用小技巧
- 聊聊 Dubbo 的 SPI 续之自适应拓展
- 聊聊 Dubbo 的 SPI

请我喝杯咖啡

WeChat Pay

Alipay

- Post author: Nicksxs
- Post link: https://nicksxs.me/2019/12/07/JVM-G1-Part-1/
- Copyright Notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.