Tue, 19 May 2009 04:05:31 -0700
6819065: G1: eliminate high serial card table clearing time
Reviewed-by: iveresov, tonyp
1.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon May 18 11:52:46 2009 -0700 1.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Tue May 19 04:05:31 2009 -0700 1.3 @@ -446,6 +446,59 @@ 1.4 gclog_or_tty->print_cr(""); 1.5 } 1.6 1.7 +void G1CollectedHeap::push_dirty_cards_region(HeapRegion* hr) 1.8 +{ 1.9 + // Claim the right to put the region on the dirty cards region list 1.10 + // by installing a self pointer. 1.11 + HeapRegion* next = hr->get_next_dirty_cards_region(); 1.12 + if (next == NULL) { 1.13 + HeapRegion* res = (HeapRegion*) 1.14 + Atomic::cmpxchg_ptr(hr, hr->next_dirty_cards_region_addr(), 1.15 + NULL); 1.16 + if (res == NULL) { 1.17 + HeapRegion* head; 1.18 + do { 1.19 + // Put the region to the dirty cards region list. 1.20 + head = _dirty_cards_region_list; 1.21 + next = (HeapRegion*) 1.22 + Atomic::cmpxchg_ptr(hr, &_dirty_cards_region_list, head); 1.23 + if (next == head) { 1.24 + assert(hr->get_next_dirty_cards_region() == hr, 1.25 + "hr->get_next_dirty_cards_region() != hr"); 1.26 + if (next == NULL) { 1.27 + // The last region in the list points to itself. 1.28 + hr->set_next_dirty_cards_region(hr); 1.29 + } else { 1.30 + hr->set_next_dirty_cards_region(next); 1.31 + } 1.32 + } 1.33 + } while (next != head); 1.34 + } 1.35 + } 1.36 +} 1.37 + 1.38 +HeapRegion* G1CollectedHeap::pop_dirty_cards_region() 1.39 +{ 1.40 + HeapRegion* head; 1.41 + HeapRegion* hr; 1.42 + do { 1.43 + head = _dirty_cards_region_list; 1.44 + if (head == NULL) { 1.45 + return NULL; 1.46 + } 1.47 + HeapRegion* new_head = head->get_next_dirty_cards_region(); 1.48 + if (head == new_head) { 1.49 + // The last region. 1.50 + new_head = NULL; 1.51 + } 1.52 + hr = (HeapRegion*)Atomic::cmpxchg_ptr(new_head, &_dirty_cards_region_list, 1.53 + head); 1.54 + } while (hr != head); 1.55 + assert(hr != NULL, "invariant"); 1.56 + hr->set_next_dirty_cards_region(NULL); 1.57 + return hr; 1.58 +} 1.59 + 1.60 void G1CollectedHeap::stop_conc_gc_threads() { 1.61 _cg1r->stop(); 1.62 _czft->stop(); 1.63 @@ -1329,7 +1382,8 @@ 1.64 _gc_time_stamp(0), 1.65 _surviving_young_words(NULL), 1.66 _in_cset_fast_test(NULL), 1.67 - _in_cset_fast_test_base(NULL) { 1.68 + _in_cset_fast_test_base(NULL), 1.69 + _dirty_cards_region_list(NULL) { 1.70 _g1h = this; // To catch bugs. 1.71 if (_process_strong_tasks == NULL || !_process_strong_tasks->valid()) { 1.72 vm_exit_during_initialization("Failed necessary allocation."); 1.73 @@ -4691,15 +4745,58 @@ 1.74 } 1.75 } 1.76 1.77 + 1.78 +class G1ParCleanupCTTask : public AbstractGangTask { 1.79 + CardTableModRefBS* _ct_bs; 1.80 + G1CollectedHeap* _g1h; 1.81 +public: 1.82 + G1ParCleanupCTTask(CardTableModRefBS* ct_bs, 1.83 + G1CollectedHeap* g1h) : 1.84 + AbstractGangTask("G1 Par Cleanup CT Task"), 1.85 + _ct_bs(ct_bs), 1.86 + _g1h(g1h) 1.87 + { } 1.88 + 1.89 + void work(int i) { 1.90 + HeapRegion* r; 1.91 + while (r = _g1h->pop_dirty_cards_region()) { 1.92 + clear_cards(r); 1.93 + } 1.94 + } 1.95 + void clear_cards(HeapRegion* r) { 1.96 + // Cards for Survivor and Scan-Only regions will be dirtied later. 1.97 + if (!r->is_scan_only() && !r->is_survivor()) { 1.98 + _ct_bs->clear(MemRegion(r->bottom(), r->end())); 1.99 + } 1.100 + } 1.101 +}; 1.102 + 1.103 + 1.104 void G1CollectedHeap::cleanUpCardTable() { 1.105 CardTableModRefBS* ct_bs = (CardTableModRefBS*) (barrier_set()); 1.106 double start = os::elapsedTime(); 1.107 1.108 - ct_bs->clear(_g1_committed); 1.109 - 1.110 + // Iterate over the dirty cards region list. 1.111 + G1ParCleanupCTTask cleanup_task(ct_bs, this); 1.112 + if (ParallelGCThreads > 0) { 1.113 + set_par_threads(workers()->total_workers()); 1.114 + workers()->run_task(&cleanup_task); 1.115 + set_par_threads(0); 1.116 + } else { 1.117 + while (_dirty_cards_region_list) { 1.118 + HeapRegion* r = _dirty_cards_region_list; 1.119 + cleanup_task.clear_cards(r); 1.120 + _dirty_cards_region_list = r->get_next_dirty_cards_region(); 1.121 + if (_dirty_cards_region_list == r) { 1.122 + // The last region. 1.123 + _dirty_cards_region_list = NULL; 1.124 + } 1.125 + r->set_next_dirty_cards_region(NULL); 1.126 + } 1.127 + } 1.128 // now, redirty the cards of the scan-only and survivor regions 1.129 // (it seemed faster to do it this way, instead of iterating over 1.130 - // all regions and then clearing / dirtying as approprite) 1.131 + // all regions and then clearing / dirtying as appropriate) 1.132 dirtyCardsForYoungRegions(ct_bs, _young_list->first_scan_only_region()); 1.133 dirtyCardsForYoungRegions(ct_bs, _young_list->first_survivor_region()); 1.134
2.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon May 18 11:52:46 2009 -0700 2.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Tue May 19 04:05:31 2009 -0700 2.3 @@ -158,6 +158,7 @@ 2.4 friend class RegionSorter; 2.5 friend class CountRCClosure; 2.6 friend class EvacPopObjClosure; 2.7 + friend class G1ParCleanupCTTask; 2.8 2.9 // Other related classes. 2.10 friend class G1MarkSweep; 2.11 @@ -1191,6 +1192,16 @@ 2.12 ConcurrentMark* concurrent_mark() const { return _cm; } 2.13 ConcurrentG1Refine* concurrent_g1_refine() const { return _cg1r; } 2.14 2.15 + // The dirty cards region list is used to record a subset of regions 2.16 + // whose cards need clearing. The list if populated during the 2.17 + // remembered set scanning and drained during the card table 2.18 + // cleanup. Although the methods are reentrant, population/draining 2.19 + // phases must not overlap. For synchronization purposes the last 2.20 + // element on the list points to itself. 2.21 + HeapRegion* _dirty_cards_region_list; 2.22 + void push_dirty_cards_region(HeapRegion* hr); 2.23 + HeapRegion* pop_dirty_cards_region(); 2.24 + 2.25 public: 2.26 void stop_conc_gc_threads(); 2.27
3.1 --- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp Mon May 18 11:52:46 2009 -0700 3.2 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp Tue May 19 04:05:31 2009 -0700 3.3 @@ -219,6 +219,7 @@ 3.4 HeapRegionRemSet* hrrs = r->rem_set(); 3.5 if (hrrs->iter_is_complete()) return false; // All done. 3.6 if (!_try_claimed && !hrrs->claim_iter()) return false; 3.7 + _g1h->push_dirty_cards_region(r); 3.8 // If we didn't return above, then 3.9 // _try_claimed || r->claim_iter() 3.10 // is true: either we're supposed to work on claimed-but-not-complete 3.11 @@ -242,6 +243,10 @@ 3.12 assert(card_region != NULL, "Yielding cards not in the heap?"); 3.13 _cards++; 3.14 3.15 + if (!card_region->is_on_dirty_cards_region_list()) { 3.16 + _g1h->push_dirty_cards_region(card_region); 3.17 + } 3.18 + 3.19 // If the card is dirty, then we will scan it during updateRS. 3.20 if (!card_region->in_collection_set() && !_ct_bs->is_card_dirty(card_index)) { 3.21 if (!_ct_bs->is_card_claimed(card_index) && _ct_bs->claim_card(card_index)) {
4.1 --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp Mon May 18 11:52:46 2009 -0700 4.2 +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp Tue May 19 04:05:31 2009 -0700 4.3 @@ -351,6 +351,7 @@ 4.4 _claimed(InitialClaimValue), _evacuation_failed(false), 4.5 _prev_marked_bytes(0), _next_marked_bytes(0), _sort_index(-1), 4.6 _young_type(NotYoung), _next_young_region(NULL), 4.7 + _next_dirty_cards_region(NULL), 4.8 _young_index_in_cset(-1), _surv_rate_group(NULL), _age_index(-1), 4.9 _rem_set(NULL), _zfs(NotZeroFilled) 4.10 {
5.1 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp Mon May 18 11:52:46 2009 -0700 5.2 +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp Tue May 19 04:05:31 2009 -0700 5.3 @@ -227,6 +227,9 @@ 5.4 // next region in the young "generation" region set 5.5 HeapRegion* _next_young_region; 5.6 5.7 + // Next region whose cards need cleaning 5.8 + HeapRegion* _next_dirty_cards_region; 5.9 + 5.10 // For parallel heapRegion traversal. 5.11 jint _claimed; 5.12 5.13 @@ -468,6 +471,11 @@ 5.14 _next_young_region = hr; 5.15 } 5.16 5.17 + HeapRegion* get_next_dirty_cards_region() const { return _next_dirty_cards_region; } 5.18 + HeapRegion** next_dirty_cards_region_addr() { return &_next_dirty_cards_region; } 5.19 + void set_next_dirty_cards_region(HeapRegion* hr) { _next_dirty_cards_region = hr; } 5.20 + bool is_on_dirty_cards_region_list() const { return get_next_dirty_cards_region() != NULL; } 5.21 + 5.22 // Allows logical separation between objects allocated before and after. 5.23 void save_marks(); 5.24