Tue, 06 Apr 2010 10:59:45 -0400
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
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.