6909756: G1: guarantee(G1CollectedHeap::heap()->mark_in_progress(),"Precondition.")

Tue, 06 Apr 2010 10:59:45 -0400

author
tonyp
date
Tue, 06 Apr 2010 10:59:45 -0400
changeset 1794
23b1b27ac76c
parent 1793
72f725c5a7be
child 1795
bda703475ded

6909756: G1: guarantee(G1CollectedHeap::heap()->mark_in_progress(),"Precondition.")
Summary: Make sure that two marking cycles do not overlap, i.e., a new one can only start after the concurrent marking thread finishes all its work. In the fix I piggy-back a couple of minor extra fixes: some general code reformatting for consistency (only around the code I modified), the removal of a field (G1CollectorPolicy::_should_initiate_conc_mark) which doesn't seem to be used at all (it's only set but never read), as well as moving the "is GC locker active" test earlier into the G1 pause / Full GC and using a more appropriate method for it.
Reviewed-by: johnc, jmasa, jcoomes, ysr

src/share/vm/gc_implementation/g1/concurrentMark.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Mon Apr 05 12:19:22 2010 -0400
     1.2 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Tue Apr 06 10:59:45 2010 -0400
     1.3 @@ -708,24 +708,46 @@
     1.4  //
     1.5  
     1.6  void ConcurrentMark::clearNextBitmap() {
     1.7 -   guarantee(!G1CollectedHeap::heap()->mark_in_progress(), "Precondition.");
     1.8 -
     1.9 -   // clear the mark bitmap (no grey objects to start with).
    1.10 -   // We need to do this in chunks and offer to yield in between
    1.11 -   // each chunk.
    1.12 -   HeapWord* start  = _nextMarkBitMap->startWord();
    1.13 -   HeapWord* end    = _nextMarkBitMap->endWord();
    1.14 -   HeapWord* cur    = start;
    1.15 -   size_t chunkSize = M;
    1.16 -   while (cur < end) {
    1.17 -     HeapWord* next = cur + chunkSize;
    1.18 -     if (next > end)
    1.19 -       next = end;
    1.20 -     MemRegion mr(cur,next);
    1.21 -     _nextMarkBitMap->clearRange(mr);
    1.22 -     cur = next;
    1.23 -     do_yield_check();
    1.24 -   }
    1.25 +  G1CollectedHeap* g1h = G1CollectedHeap::heap();
    1.26 +  G1CollectorPolicy* g1p = g1h->g1_policy();
    1.27 +
    1.28 +  // Make sure that the concurrent mark thread looks to still be in
    1.29 +  // the current cycle.
    1.30 +  guarantee(cmThread()->during_cycle(), "invariant");
    1.31 +
    1.32 +  // We are finishing up the current cycle by clearing the next
    1.33 +  // marking bitmap and getting it ready for the next cycle. During
    1.34 +  // this time no other cycle can start. So, let's make sure that this
    1.35 +  // is the case.
    1.36 +  guarantee(!g1h->mark_in_progress(), "invariant");
    1.37 +
    1.38 +  // clear the mark bitmap (no grey objects to start with).
    1.39 +  // We need to do this in chunks and offer to yield in between
    1.40 +  // each chunk.
    1.41 +  HeapWord* start  = _nextMarkBitMap->startWord();
    1.42 +  HeapWord* end    = _nextMarkBitMap->endWord();
    1.43 +  HeapWord* cur    = start;
    1.44 +  size_t chunkSize = M;
    1.45 +  while (cur < end) {
    1.46 +    HeapWord* next = cur + chunkSize;
    1.47 +    if (next > end)
    1.48 +      next = end;
    1.49 +    MemRegion mr(cur,next);
    1.50 +    _nextMarkBitMap->clearRange(mr);
    1.51 +    cur = next;
    1.52 +    do_yield_check();
    1.53 +
    1.54 +    // Repeat the asserts from above. We'll do them as asserts here to
    1.55 +    // minimize their overhead on the product. However, we'll have
    1.56 +    // them as guarantees at the beginning / end of the bitmap
    1.57 +    // clearing to get some checking in the product.
    1.58 +    assert(cmThread()->during_cycle(), "invariant");
    1.59 +    assert(!g1h->mark_in_progress(), "invariant");
    1.60 +  }
    1.61 +
    1.62 +  // Repeat the asserts from above.
    1.63 +  guarantee(cmThread()->during_cycle(), "invariant");
    1.64 +  guarantee(!g1h->mark_in_progress(), "invariant");
    1.65  }
    1.66  
    1.67  class NoteStartOfMarkHRClosure: public HeapRegionClosure {
     2.1 --- a/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp	Mon Apr 05 12:19:22 2010 -0400
     2.2 +++ b/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp	Tue Apr 06 10:59:45 2010 -0400
     2.3 @@ -42,8 +42,8 @@
     2.4  
     2.5   private:
     2.6    ConcurrentMark*                  _cm;
     2.7 -  bool                             _started;
     2.8 -  bool                             _in_progress;
     2.9 +  volatile bool                    _started;
    2.10 +  volatile bool                    _in_progress;
    2.11  
    2.12    void sleepBeforeNextCycle();
    2.13  
    2.14 @@ -67,15 +67,25 @@
    2.15    // Counting virtual time so far.
    2.16    double vtime_count_accum() { return _vtime_count_accum; }
    2.17  
    2.18 -  ConcurrentMark* cm()                           { return _cm;     }
    2.19 +  ConcurrentMark* cm()     { return _cm; }
    2.20  
    2.21 -  void            set_started()                  { _started = true;   }
    2.22 -  void            clear_started()                { _started = false;  }
    2.23 -  bool            started()                      { return _started;   }
    2.24 +  void set_started()       { _started = true;  }
    2.25 +  void clear_started()     { _started = false; }
    2.26 +  bool started()           { return _started;  }
    2.27  
    2.28 -  void            set_in_progress()              { _in_progress = true;   }
    2.29 -  void            clear_in_progress()            { _in_progress = false;  }
    2.30 -  bool            in_progress()                  { return _in_progress;   }
    2.31 +  void set_in_progress()   { _in_progress = true;  }
    2.32 +  void clear_in_progress() { _in_progress = false; }
    2.33 +  bool in_progress()       { return _in_progress;  }
    2.34 +
    2.35 +  // This flag returns true from the moment a marking cycle is
    2.36 +  // initiated (during the initial-mark pause when started() is set)
    2.37 +  // to the moment when the cycle completes (just after the next
    2.38 +  // marking bitmap has been cleared and in_progress() is
    2.39 +  // cleared). While this flag is true we will not start another cycle
    2.40 +  // so that cycles do not overlap. We cannot use just in_progress()
    2.41 +  // as the CM thread might take some time to wake up before noticing
    2.42 +  // that started() is set and set in_progress().
    2.43 +  bool during_cycle()      { return started() || in_progress(); }
    2.44  
    2.45    // Yield for GC
    2.46    void            yield();
     3.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Mon Apr 05 12:19:22 2010 -0400
     3.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Tue Apr 06 10:59:45 2010 -0400
     3.3 @@ -902,6 +902,10 @@
     3.4  
     3.5  void G1CollectedHeap::do_collection(bool full, bool clear_all_soft_refs,
     3.6                                      size_t word_size) {
     3.7 +  if (GC_locker::check_active_before_gc()) {
     3.8 +    return; // GC is disabled (e.g. JNI GetXXXCritical operation)
     3.9 +  }
    3.10 +
    3.11    ResourceMark rm;
    3.12  
    3.13    if (PrintHeapAtGC) {
    3.14 @@ -916,10 +920,6 @@
    3.15    assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
    3.16    assert(Thread::current() == VMThread::vm_thread(), "should be in vm thread");
    3.17  
    3.18 -  if (GC_locker::is_active()) {
    3.19 -    return; // GC is disabled (e.g. JNI GetXXXCritical operation)
    3.20 -  }
    3.21 -
    3.22    {
    3.23      IsGCActiveMark x;
    3.24  
    3.25 @@ -2658,6 +2658,10 @@
    3.26  
    3.27  void
    3.28  G1CollectedHeap::do_collection_pause_at_safepoint() {
    3.29 +  if (GC_locker::check_active_before_gc()) {
    3.30 +    return; // GC is disabled (e.g. JNI GetXXXCritical operation)
    3.31 +  }
    3.32 +
    3.33    if (PrintHeapAtGC) {
    3.34      Universe::print_heap_before_gc();
    3.35    }
    3.36 @@ -2665,6 +2669,11 @@
    3.37    {
    3.38      ResourceMark rm;
    3.39  
    3.40 +    // This call will decide whether this pause is an initial-mark
    3.41 +    // pause. If it is, during_initial_mark_pause() will return true
    3.42 +    // for the duration of this pause.
    3.43 +    g1_policy()->decide_on_conc_mark_initiation();
    3.44 +
    3.45      char verbose_str[128];
    3.46      sprintf(verbose_str, "GC pause ");
    3.47      if (g1_policy()->in_young_gc_mode()) {
    3.48 @@ -2673,7 +2682,7 @@
    3.49        else
    3.50          strcat(verbose_str, "(partial)");
    3.51      }
    3.52 -    if (g1_policy()->should_initiate_conc_mark())
    3.53 +    if (g1_policy()->during_initial_mark_pause())
    3.54        strcat(verbose_str, " (initial-mark)");
    3.55  
    3.56      // if PrintGCDetails is on, we'll print long statistics information
    3.57 @@ -2697,10 +2706,6 @@
    3.58               "young list should be well formed");
    3.59      }
    3.60  
    3.61 -    if (GC_locker::is_active()) {
    3.62 -      return; // GC is disabled (e.g. JNI GetXXXCritical operation)
    3.63 -    }
    3.64 -
    3.65      bool abandoned = false;
    3.66      { // Call to jvmpi::post_class_unload_events must occur outside of active GC
    3.67        IsGCActiveMark x;
    3.68 @@ -2756,7 +2761,7 @@
    3.69        _young_list->print();
    3.70  #endif // SCAN_ONLY_VERBOSE
    3.71  
    3.72 -      if (g1_policy()->should_initiate_conc_mark()) {
    3.73 +      if (g1_policy()->during_initial_mark_pause()) {
    3.74          concurrent_mark()->checkpointRootsInitialPre();
    3.75        }
    3.76        save_marks();
    3.77 @@ -2858,7 +2863,7 @@
    3.78        }
    3.79  
    3.80        if (g1_policy()->in_young_gc_mode() &&
    3.81 -          g1_policy()->should_initiate_conc_mark()) {
    3.82 +          g1_policy()->during_initial_mark_pause()) {
    3.83          concurrent_mark()->checkpointRootsInitialPost();
    3.84          set_marking_started();
    3.85          // CAUTION: after the doConcurrentMark() call below,
    3.86 @@ -3977,7 +3982,7 @@
    3.87      OopsInHeapRegionClosure        *scan_perm_cl;
    3.88      OopsInHeapRegionClosure        *scan_so_cl;
    3.89  
    3.90 -    if (_g1h->g1_policy()->should_initiate_conc_mark()) {
    3.91 +    if (_g1h->g1_policy()->during_initial_mark_pause()) {
    3.92        scan_root_cl = &scan_mark_root_cl;
    3.93        scan_perm_cl = &scan_mark_perm_cl;
    3.94        scan_so_cl   = &scan_mark_heap_rs_cl;
    3.95 @@ -4140,7 +4145,7 @@
    3.96    FilterAndMarkInHeapRegionAndIntoCSClosure scan_and_mark(this, &boc, concurrent_mark());
    3.97  
    3.98    OopsInHeapRegionClosure *foc;
    3.99 -  if (g1_policy()->should_initiate_conc_mark())
   3.100 +  if (g1_policy()->during_initial_mark_pause())
   3.101      foc = &scan_and_mark;
   3.102    else
   3.103      foc = &scan_only;
     4.1 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp	Mon Apr 05 12:19:22 2010 -0400
     4.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp	Tue Apr 06 10:59:45 2010 -0400
     4.3 @@ -178,8 +178,8 @@
     4.4    // so the hack is to do the cast  QQQ FIXME
     4.5    _pauses_btwn_concurrent_mark((size_t)G1PausesBtwnConcMark),
     4.6    _n_marks_since_last_pause(0),
     4.7 -  _conc_mark_initiated(false),
     4.8 -  _should_initiate_conc_mark(false),
     4.9 +  _initiate_conc_mark_if_possible(false),
    4.10 +  _during_initial_mark_pause(false),
    4.11    _should_revert_to_full_young_gcs(false),
    4.12    _last_full_young_gc(false),
    4.13  
    4.14 @@ -793,7 +793,7 @@
    4.15                             elapsed_time_ms,
    4.16                             calculations,
    4.17                             full_young_gcs() ? "full" : "partial",
    4.18 -                           should_initiate_conc_mark() ? " i-m" : "",
    4.19 +                           during_initial_mark_pause() ? " i-m" : "",
    4.20                             _in_marking_window,
    4.21                             _in_marking_window_im);
    4.22  #endif // TRACE_CALC_YOUNG_CONFIG
    4.23 @@ -1040,7 +1040,8 @@
    4.24    set_full_young_gcs(true);
    4.25    _last_full_young_gc = false;
    4.26    _should_revert_to_full_young_gcs = false;
    4.27 -  _should_initiate_conc_mark = false;
    4.28 +  clear_initiate_conc_mark_if_possible();
    4.29 +  clear_during_initial_mark_pause();
    4.30    _known_garbage_bytes = 0;
    4.31    _known_garbage_ratio = 0.0;
    4.32    _in_marking_window = false;
    4.33 @@ -1186,7 +1187,8 @@
    4.34  void G1CollectorPolicy::record_concurrent_mark_init_end_pre(double
    4.35                                                     mark_init_elapsed_time_ms) {
    4.36    _during_marking = true;
    4.37 -  _should_initiate_conc_mark = false;
    4.38 +  assert(!initiate_conc_mark_if_possible(), "we should have cleared it by now");
    4.39 +  clear_during_initial_mark_pause();
    4.40    _cur_mark_stop_world_time_ms = mark_init_elapsed_time_ms;
    4.41  }
    4.42  
    4.43 @@ -1257,7 +1259,6 @@
    4.44    }
    4.45    _n_pauses_at_mark_end = _n_pauses;
    4.46    _n_marks_since_last_pause++;
    4.47 -  _conc_mark_initiated = false;
    4.48  }
    4.49  
    4.50  void
    4.51 @@ -1453,17 +1454,24 @@
    4.52  #endif // PRODUCT
    4.53  
    4.54    if (in_young_gc_mode()) {
    4.55 -    last_pause_included_initial_mark = _should_initiate_conc_mark;
    4.56 +    last_pause_included_initial_mark = during_initial_mark_pause();
    4.57      if (last_pause_included_initial_mark)
    4.58        record_concurrent_mark_init_end_pre(0.0);
    4.59  
    4.60      size_t min_used_targ =
    4.61        (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent;
    4.62  
    4.63 -    if (cur_used_bytes > min_used_targ) {
    4.64 -      if (cur_used_bytes <= _prev_collection_pause_used_at_end_bytes) {
    4.65 -      } else if (!_g1->mark_in_progress() && !_last_full_young_gc) {
    4.66 -        _should_initiate_conc_mark = true;
    4.67 +
    4.68 +    if (!_g1->mark_in_progress() && !_last_full_young_gc) {
    4.69 +      assert(!last_pause_included_initial_mark, "invariant");
    4.70 +      if (cur_used_bytes > min_used_targ &&
    4.71 +          cur_used_bytes > _prev_collection_pause_used_at_end_bytes) {
    4.72 +        assert(!during_initial_mark_pause(), "we should not see this here");
    4.73 +
    4.74 +        // Note: this might have already been set, if during the last
    4.75 +        // pause we decided to start a cycle but at the beginning of
    4.76 +        // this pause we decided to postpone it. That's OK.
    4.77 +        set_initiate_conc_mark_if_possible();
    4.78        }
    4.79      }
    4.80  
    4.81 @@ -1754,7 +1762,7 @@
    4.82  
    4.83    bool new_in_marking_window = _in_marking_window;
    4.84    bool new_in_marking_window_im = false;
    4.85 -  if (_should_initiate_conc_mark) {
    4.86 +  if (during_initial_mark_pause()) {
    4.87      new_in_marking_window = true;
    4.88      new_in_marking_window_im = true;
    4.89    }
    4.90 @@ -2173,7 +2181,13 @@
    4.91    if (predicted_time_ms > _expensive_region_limit_ms) {
    4.92      if (!in_young_gc_mode()) {
    4.93          set_full_young_gcs(true);
    4.94 -      _should_initiate_conc_mark = true;
    4.95 +        // We might want to do something different here. However,
    4.96 +        // right now we don't support the non-generational G1 mode
    4.97 +        // (and in fact we are planning to remove the associated code,
    4.98 +        // see CR 6814390). So, let's leave it as is and this will be
    4.99 +        // removed some time in the future
   4.100 +        ShouldNotReachHere();
   4.101 +        set_during_initial_mark_pause();
   4.102      } else
   4.103        // no point in doing another partial one
   4.104        _should_revert_to_full_young_gcs = true;
   4.105 @@ -2697,6 +2711,50 @@
   4.106  #endif
   4.107  
   4.108  void
   4.109 +G1CollectorPolicy::decide_on_conc_mark_initiation() {
   4.110 +  // We are about to decide on whether this pause will be an
   4.111 +  // initial-mark pause.
   4.112 +
   4.113 +  // First, during_initial_mark_pause() should not be already set. We
   4.114 +  // will set it here if we have to. However, it should be cleared by
   4.115 +  // the end of the pause (it's only set for the duration of an
   4.116 +  // initial-mark pause).
   4.117 +  assert(!during_initial_mark_pause(), "pre-condition");
   4.118 +
   4.119 +  if (initiate_conc_mark_if_possible()) {
   4.120 +    // We had noticed on a previous pause that the heap occupancy has
   4.121 +    // gone over the initiating threshold and we should start a
   4.122 +    // concurrent marking cycle. So we might initiate one.
   4.123 +
   4.124 +    bool during_cycle = _g1->concurrent_mark()->cmThread()->during_cycle();
   4.125 +    if (!during_cycle) {
   4.126 +      // The concurrent marking thread is not "during a cycle", i.e.,
   4.127 +      // it has completed the last one. So we can go ahead and
   4.128 +      // initiate a new cycle.
   4.129 +
   4.130 +      set_during_initial_mark_pause();
   4.131 +
   4.132 +      // And we can now clear initiate_conc_mark_if_possible() as
   4.133 +      // we've already acted on it.
   4.134 +      clear_initiate_conc_mark_if_possible();
   4.135 +    } else {
   4.136 +      // The concurrent marking thread is still finishing up the
   4.137 +      // previous cycle. If we start one right now the two cycles
   4.138 +      // overlap. In particular, the concurrent marking thread might
   4.139 +      // be in the process of clearing the next marking bitmap (which
   4.140 +      // we will use for the next cycle if we start one). Starting a
   4.141 +      // cycle now will be bad given that parts of the marking
   4.142 +      // information might get cleared by the marking thread. And we
   4.143 +      // cannot wait for the marking thread to finish the cycle as it
   4.144 +      // periodically yields while clearing the next marking bitmap
   4.145 +      // and, if it's in a yield point, it's waiting for us to
   4.146 +      // finish. So, at this point we will not start a cycle and we'll
   4.147 +      // let the concurrent marking thread complete the last one.
   4.148 +    }
   4.149 +  }
   4.150 +}
   4.151 +
   4.152 +void
   4.153  G1CollectorPolicy_BestRegionsFirst::
   4.154  record_collection_pause_start(double start_time_sec, size_t start_used) {
   4.155    G1CollectorPolicy::record_collection_pause_start(start_time_sec, start_used);
     5.1 --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp	Mon Apr 05 12:19:22 2010 -0400
     5.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp	Tue Apr 06 10:59:45 2010 -0400
     5.3 @@ -724,11 +724,31 @@
     5.4  
     5.5    size_t _n_marks_since_last_pause;
     5.6  
     5.7 -  // True iff CM has been initiated.
     5.8 -  bool _conc_mark_initiated;
     5.9 +  // At the end of a pause we check the heap occupancy and we decide
    5.10 +  // whether we will start a marking cycle during the next pause. If
    5.11 +  // we decide that we want to do that, we will set this parameter to
    5.12 +  // true. So, this parameter will stay true between the end of a
    5.13 +  // pause and the beginning of a subsequent pause (not necessarily
    5.14 +  // the next one, see the comments on the next field) when we decide
    5.15 +  // that we will indeed start a marking cycle and do the initial-mark
    5.16 +  // work.
    5.17 +  volatile bool _initiate_conc_mark_if_possible;
    5.18  
    5.19 -  // True iff CM should be initiated
    5.20 -  bool _should_initiate_conc_mark;
    5.21 +  // If initiate_conc_mark_if_possible() is set at the beginning of a
    5.22 +  // pause, it is a suggestion that the pause should start a marking
    5.23 +  // cycle by doing the initial-mark work. However, it is possible
    5.24 +  // that the concurrent marking thread is still finishing up the
    5.25 +  // previous marking cycle (e.g., clearing the next marking
    5.26 +  // bitmap). If that is the case we cannot start a new cycle and
    5.27 +  // we'll have to wait for the concurrent marking thread to finish
    5.28 +  // what it is doing. In this case we will postpone the marking cycle
    5.29 +  // initiation decision for the next pause. When we eventually decide
    5.30 +  // to start a cycle, we will set _during_initial_mark_pause which
    5.31 +  // will stay true until the end of the initial-mark pause and it's
    5.32 +  // the condition that indicates that a pause is doing the
    5.33 +  // initial-mark work.
    5.34 +  volatile bool _during_initial_mark_pause;
    5.35 +
    5.36    bool _should_revert_to_full_young_gcs;
    5.37    bool _last_full_young_gc;
    5.38  
    5.39 @@ -981,9 +1001,21 @@
    5.40    // Add "hr" to the CS.
    5.41    void add_to_collection_set(HeapRegion* hr);
    5.42  
    5.43 -  bool should_initiate_conc_mark()      { return _should_initiate_conc_mark; }
    5.44 -  void set_should_initiate_conc_mark()  { _should_initiate_conc_mark = true; }
    5.45 -  void unset_should_initiate_conc_mark(){ _should_initiate_conc_mark = false; }
    5.46 +  bool initiate_conc_mark_if_possible()       { return _initiate_conc_mark_if_possible;  }
    5.47 +  void set_initiate_conc_mark_if_possible()   { _initiate_conc_mark_if_possible = true;  }
    5.48 +  void clear_initiate_conc_mark_if_possible() { _initiate_conc_mark_if_possible = false; }
    5.49 +
    5.50 +  bool during_initial_mark_pause()      { return _during_initial_mark_pause;  }
    5.51 +  void set_during_initial_mark_pause()  { _during_initial_mark_pause = true;  }
    5.52 +  void clear_during_initial_mark_pause(){ _during_initial_mark_pause = false; }
    5.53 +
    5.54 +  // This is called at the very beginning of an evacuation pause (it
    5.55 +  // has to be the first thing that the pause does). If
    5.56 +  // initiate_conc_mark_if_possible() is true, and the concurrent
    5.57 +  // marking thread has completed its work during the previous cycle,
    5.58 +  // it will set during_initial_mark_pause() to so that the pause does
    5.59 +  // the initial-mark work and start a marking cycle.
    5.60 +  void decide_on_conc_mark_initiation();
    5.61  
    5.62    // If an expansion would be appropriate, because recent GC overhead had
    5.63    // exceeded the desired limit, return an amount to expand by.

mercurial