Skip to content

Commit e9c1782

Browse files
author
Thomas Schatzl
committed
8252752: Clear card table for old regions during scan in G1
Reviewed-by: kbarrett, iwalulya, ayang
1 parent 276fcee commit e9c1782

File tree

6 files changed

+109
-40
lines changed

6 files changed

+109
-40
lines changed

src/hotspot/share/gc/g1/g1CardTable.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class G1CardTable : public CardTable {
8484
}
8585

8686
static CardValue g1_young_card_val() { return g1_young_gen; }
87+
static CardValue g1_scanned_card_val() { return g1_card_already_scanned; }
8788

8889
void verify_g1_young_region(MemRegion mr) PRODUCT_RETURN;
8990
void g1_mark_as_young(const MemRegion& mr);
@@ -103,8 +104,8 @@ class G1CardTable : public CardTable {
103104
// be inaccurate as it does not perform the dirtying atomically.
104105
inline size_t mark_region_dirty(size_t start_card_index, size_t num_cards);
105106

106-
// Mark the given range of cards as Scanned. All of these cards must be Dirty.
107-
inline void mark_as_scanned(size_t start_card_index, size_t num_cards);
107+
// Change the given range of dirty cards to "which". All of these cards must be Dirty.
108+
inline void change_dirty_cards_to(size_t start_card_index, size_t num_cards, CardValue which);
108109

109110
inline uint region_idx_for(CardValue* p);
110111

src/hotspot/share/gc/g1/g1CardTable.inline.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ inline size_t G1CardTable::mark_region_dirty(size_t start_card_index, size_t num
7777
return result;
7878
}
7979

80-
inline void G1CardTable::mark_as_scanned(size_t start_card_index, size_t num_cards) {
80+
inline void G1CardTable::change_dirty_cards_to(size_t start_card_index, size_t num_cards, CardValue which) {
8181
CardValue* start = &_byte_map[start_card_index];
8282
CardValue* const end = start + num_cards;
8383
while (start < end) {
8484
CardValue value = *start;
8585
assert(value == dirty_card_val(),
8686
"Must have been dirty %d start " PTR_FORMAT " " PTR_FORMAT, value, p2i(start), p2i(end));
87-
*start++ = g1_card_already_scanned;
87+
*start++ = which;
8888
}
8989
}
9090

src/hotspot/share/gc/g1/g1CollectedHeap.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,10 +3045,11 @@ void G1CollectedHeap::do_collection_pause_at_safepoint_helper(double target_paus
30453045
collection_set()->optional_region_length());
30463046
pre_evacuate_collection_set(evacuation_info, &per_thread_states);
30473047

3048+
bool may_do_optional_evacuation = _collection_set.optional_region_length() != 0;
30483049
// Actually do the work...
3049-
evacuate_initial_collection_set(&per_thread_states);
3050+
evacuate_initial_collection_set(&per_thread_states, may_do_optional_evacuation);
30503051

3051-
if (_collection_set.optional_region_length() != 0) {
3052+
if (may_do_optional_evacuation) {
30523053
evacuate_optional_collection_set(&per_thread_states);
30533054
}
30543055
post_evacuate_collection_set(evacuation_info, &rdcqs, &per_thread_states);
@@ -3814,10 +3815,11 @@ class G1EvacuateRegionsBaseTask : public AbstractGangTask {
38143815

38153816
class G1EvacuateRegionsTask : public G1EvacuateRegionsBaseTask {
38163817
G1RootProcessor* _root_processor;
3818+
bool _has_optional_evacuation_work;
38173819

38183820
void scan_roots(G1ParScanThreadState* pss, uint worker_id) {
38193821
_root_processor->evacuate_roots(pss, worker_id);
3820-
_g1h->rem_set()->scan_heap_roots(pss, worker_id, G1GCPhaseTimes::ScanHR, G1GCPhaseTimes::ObjCopy);
3822+
_g1h->rem_set()->scan_heap_roots(pss, worker_id, G1GCPhaseTimes::ScanHR, G1GCPhaseTimes::ObjCopy, _has_optional_evacuation_work);
38213823
_g1h->rem_set()->scan_collection_set_regions(pss, worker_id, G1GCPhaseTimes::ScanHR, G1GCPhaseTimes::CodeRoots, G1GCPhaseTimes::ObjCopy);
38223824
}
38233825

@@ -3838,13 +3840,16 @@ class G1EvacuateRegionsTask : public G1EvacuateRegionsBaseTask {
38383840
G1ParScanThreadStateSet* per_thread_states,
38393841
G1ScannerTasksQueueSet* task_queues,
38403842
G1RootProcessor* root_processor,
3841-
uint num_workers) :
3843+
uint num_workers,
3844+
bool has_optional_evacuation_work) :
38423845
G1EvacuateRegionsBaseTask("G1 Evacuate Regions", per_thread_states, task_queues, num_workers),
3843-
_root_processor(root_processor)
3846+
_root_processor(root_processor),
3847+
_has_optional_evacuation_work(has_optional_evacuation_work)
38443848
{ }
38453849
};
38463850

3847-
void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states) {
3851+
void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states,
3852+
bool has_optional_evacuation_work) {
38483853
G1GCPhaseTimes* p = phase_times();
38493854

38503855
{
@@ -3859,7 +3864,12 @@ void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* p
38593864
Ticks start_processing = Ticks::now();
38603865
{
38613866
G1RootProcessor root_processor(this, num_workers);
3862-
G1EvacuateRegionsTask g1_par_task(this, per_thread_states, _task_queues, &root_processor, num_workers);
3867+
G1EvacuateRegionsTask g1_par_task(this,
3868+
per_thread_states,
3869+
_task_queues,
3870+
&root_processor,
3871+
num_workers,
3872+
has_optional_evacuation_work);
38633873
task_time = run_task_timed(&g1_par_task);
38643874
// Closing the inner scope will execute the destructor for the G1RootProcessor object.
38653875
// To extract its code root fixup time we measure total time of this scope and
@@ -3869,12 +3879,14 @@ void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* p
38693879

38703880
p->record_initial_evac_time(task_time.seconds() * 1000.0);
38713881
p->record_or_add_code_root_fixup_time((total_processing - task_time).seconds() * 1000.0);
3882+
3883+
rem_set()->complete_evac_phase(has_optional_evacuation_work);
38723884
}
38733885

38743886
class G1EvacuateOptionalRegionsTask : public G1EvacuateRegionsBaseTask {
38753887

38763888
void scan_roots(G1ParScanThreadState* pss, uint worker_id) {
3877-
_g1h->rem_set()->scan_heap_roots(pss, worker_id, G1GCPhaseTimes::OptScanHR, G1GCPhaseTimes::OptObjCopy);
3889+
_g1h->rem_set()->scan_heap_roots(pss, worker_id, G1GCPhaseTimes::OptScanHR, G1GCPhaseTimes::OptObjCopy, true /* remember_already_scanned_cards */);
38783890
_g1h->rem_set()->scan_collection_set_regions(pss, worker_id, G1GCPhaseTimes::OptScanHR, G1GCPhaseTimes::OptCodeRoots, G1GCPhaseTimes::OptObjCopy);
38793891
}
38803892

@@ -3934,6 +3946,8 @@ void G1CollectedHeap::evacuate_optional_collection_set(G1ParScanThreadStateSet*
39343946
evacuate_next_optional_regions(per_thread_states);
39353947
phase_times()->record_or_add_optional_evac_time((Ticks::now() - start).seconds() * 1000.0);
39363948
}
3949+
3950+
rem_set()->complete_evac_phase(true /* has_more_than_one_evacuation_phase */);
39373951
}
39383952

39393953
_collection_set.abandon_optional_collection_set(per_thread_states);

src/hotspot/share/gc/g1/g1CollectedHeap.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,23 @@ class G1CollectedHeap : public CollectedHeap {
785785
void calculate_collection_set(G1EvacuationInfo& evacuation_info, double target_pause_time_ms);
786786

787787
// Actually do the work of evacuating the parts of the collection set.
788-
void evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states);
788+
// The has_optional_evacuation_work flag for the initial collection set
789+
// evacuation indicates whether one or more optional evacuation steps may
790+
// follow.
791+
// If not set, G1 can avoid clearing the card tables of regions that we scan
792+
// for roots from the heap: when scanning the card table for dirty cards after
793+
// all remembered sets have been dumped onto it, for optional evacuation we
794+
// mark these cards as "Scanned" to know that we do not need to re-scan them
795+
// in the additional optional evacuation passes. This means that in the "Clear
796+
// Card Table" phase we need to clear those marks. However, if there is no
797+
// optional evacuation, g1 can immediately clean the dirty cards it encounters
798+
// as nobody else will be looking at them again, saving the clear card table
799+
// work later.
800+
// This case is very common (young only collections and most mixed gcs), so
801+
// depending on the ratio between scanned and evacuated regions (which g1 always
802+
// needs to clear), this is a big win.
803+
void evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states,
804+
bool has_optional_evacuation_work);
789805
void evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states);
790806
private:
791807
// Evacuate the next set of optional regions.

src/hotspot/share/gc/g1/g1RemSet.cpp

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,17 @@ class G1RemSetScanState : public CHeapObj<mtGC> {
131131
}
132132

133133
private:
134-
// The complete set of regions which card table needs to be cleared at the end of GC because
135-
// we scribbled all over them.
134+
// The complete set of regions which card table needs to be cleared at the end
135+
// of GC because we scribbled over these card tables.
136+
//
137+
// Regions may be added for two reasons:
138+
// - they were part of the collection set: they may contain g1_young_card_val
139+
// or regular card marks that we never scan so we must always clear their card
140+
// table
141+
// - or in case g1 does an optional evacuation pass, g1 marks the cards in there
142+
// as g1_scanned_card_val. If G1 only did an initial evacuation pass, the
143+
// scanning already cleared these cards. In that case they are not in this set
144+
// at the end of the collection.
136145
G1DirtyRegions* _all_dirty_regions;
137146
// The set of regions which card table needs to be scanned for new dirty cards
138147
// in the current evacuation pass.
@@ -321,16 +330,22 @@ class G1RemSetScanState : public CHeapObj<mtGC> {
321330
}
322331

323332
void prepare_for_merge_heap_roots() {
324-
_all_dirty_regions->merge(_next_dirty_regions);
333+
assert(_next_dirty_regions->size() == 0, "next dirty regions must be empty");
325334

326-
_next_dirty_regions->reset();
327335
for (size_t i = 0; i < _max_reserved_regions; i++) {
328336
_card_table_scan_state[i] = 0;
329337
}
330338

331339
::memset(_region_scan_chunks, false, _num_total_scan_chunks * sizeof(*_region_scan_chunks));
332340
}
333341

342+
void complete_evac_phase(bool merge_dirty_regions) {
343+
if (merge_dirty_regions) {
344+
_all_dirty_regions->merge(_next_dirty_regions);
345+
}
346+
_next_dirty_regions->reset();
347+
}
348+
334349
// Returns whether the given region contains cards we need to scan. The remembered
335350
// set and other sources may contain cards that
336351
// - are in uncommitted regions
@@ -374,8 +389,6 @@ class G1RemSetScanState : public CHeapObj<mtGC> {
374389
}
375390

376391
void cleanup(WorkGang* workers) {
377-
_all_dirty_regions->merge(_next_dirty_regions);
378-
379392
clear_card_table(workers);
380393

381394
delete _all_dirty_regions;
@@ -448,7 +461,7 @@ class G1RemSetScanState : public CHeapObj<mtGC> {
448461
#ifdef ASSERT
449462
HeapRegion* hr = G1CollectedHeap::heap()->region_at(region);
450463
assert(hr->in_collection_set(),
451-
"Only add young regions to all dirty regions directly but %u is %s",
464+
"Only add collection set regions to all dirty regions directly but %u is %s",
452465
hr->hrm_index(), hr->get_short_type_str());
453466
#endif
454467
_all_dirty_regions->add_dirty_region(region);
@@ -641,6 +654,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure {
641654
// The address to which this thread already scanned (walked the heap) up to during
642655
// card scanning (exclusive).
643656
HeapWord* _scanned_to;
657+
G1CardTable::CardValue _scanned_card_value;
644658

645659
HeapWord* scan_memregion(uint region_idx_for_card, MemRegion mr) {
646660
HeapRegion* const card_region = _g1h->region_at(region_idx_for_card);
@@ -677,7 +691,7 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure {
677691
}
678692

679693
ALWAYSINLINE void do_card_block(uint const region_idx, size_t const first_card, size_t const num_cards) {
680-
_ct->mark_as_scanned(first_card, num_cards);
694+
_ct->change_dirty_cards_to(first_card, num_cards, _scanned_card_value);
681695
do_claimed_block(region_idx, first_card, num_cards);
682696
_blocks_scanned++;
683697
}
@@ -727,7 +741,8 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure {
727741
G1ScanHRForRegionClosure(G1RemSetScanState* scan_state,
728742
G1ParScanThreadState* pss,
729743
uint worker_id,
730-
G1GCPhaseTimes::GCParPhases phase) :
744+
G1GCPhaseTimes::GCParPhases phase,
745+
bool remember_already_scanned_cards) :
731746
_g1h(G1CollectedHeap::heap()),
732747
_ct(_g1h->card_table()),
733748
_bot(_g1h->bot()),
@@ -740,7 +755,9 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure {
740755
_chunks_claimed(0),
741756
_rem_set_root_scan_time(),
742757
_rem_set_trim_partially_time(),
743-
_scanned_to(NULL) {
758+
_scanned_to(NULL),
759+
_scanned_card_value(remember_already_scanned_cards ? G1CardTable::g1_scanned_card_val()
760+
: G1CardTable::clean_card_val()) {
744761
}
745762

746763
bool do_heap_region(HeapRegion* r) {
@@ -765,10 +782,11 @@ class G1ScanHRForRegionClosure : public HeapRegionClosure {
765782
};
766783

767784
void G1RemSet::scan_heap_roots(G1ParScanThreadState* pss,
768-
uint worker_id,
769-
G1GCPhaseTimes::GCParPhases scan_phase,
770-
G1GCPhaseTimes::GCParPhases objcopy_phase) {
771-
G1ScanHRForRegionClosure cl(_scan_state, pss, worker_id, scan_phase);
785+
uint worker_id,
786+
G1GCPhaseTimes::GCParPhases scan_phase,
787+
G1GCPhaseTimes::GCParPhases objcopy_phase,
788+
bool remember_already_scanned_cards) {
789+
G1ScanHRForRegionClosure cl(_scan_state, pss, worker_id, scan_phase, remember_already_scanned_cards);
772790
_scan_state->iterate_dirty_regions_from(&cl, worker_id);
773791

774792
G1GCPhaseTimes* p = _g1p->phase_times();
@@ -891,18 +909,11 @@ void G1RemSet::scan_collection_set_regions(G1ParScanThreadState* pss,
891909
void G1RemSet::prepare_region_for_scan(HeapRegion* region) {
892910
uint hrm_index = region->hrm_index();
893911

894-
if (region->in_collection_set()) {
895-
// Young regions had their card table marked as young at their allocation;
896-
// we need to make sure that these marks are cleared at the end of GC, *but*
897-
// they should not be scanned for cards.
898-
// So directly add them to the "all_dirty_regions".
899-
// Same for regions in the (initial) collection set: they may contain cards from
900-
// the log buffers, make sure they are cleaned.
901-
_scan_state->add_all_dirty_region(hrm_index);
902-
} else if (region->is_old_or_humongous_or_archive()) {
912+
if (region->is_old_or_humongous_or_archive()) {
903913
_scan_state->set_scan_top(hrm_index, region->top());
904914
} else {
905-
assert(region->is_free(), "Should only be free region at this point %s", region->get_type_str());
915+
assert(region->in_collection_set() || region->is_free(),
916+
"Should only be free or in the collection set at this point %s", region->get_type_str());
906917
}
907918
}
908919

@@ -984,13 +995,34 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
984995
}
985996
}
986997

987-
virtual bool do_heap_region(HeapRegion* r) {
998+
// Helper to put the remembered set cards for these regions onto the card
999+
// table.
1000+
//
1001+
// Called directly for humongous starts regions because we should not add
1002+
// humongous eager reclaim candidates to the "all" list of regions to
1003+
// clear the card table by default as we do not know yet whether this region
1004+
// will be reclaimed (and reused).
1005+
// If the humongous region contains dirty cards, g1 will scan them
1006+
// because dumping the remembered set entries onto the card table will add
1007+
// the humongous region to the "dirty" region list to scan. Then scanning
1008+
// either clears the card during scan (if there is only an initial evacuation
1009+
// pass) or the "dirty" list will be merged with the "all" list later otherwise.
1010+
// (And there is no problem either way if the region does not contain dirty
1011+
// cards).
1012+
void dump_rem_set_for_region(HeapRegion* r) {
9881013
assert(r->in_collection_set() || r->is_starts_humongous(), "must be");
9891014

9901015
HeapRegionRemSet* rem_set = r->rem_set();
9911016
if (!rem_set->is_empty()) {
9921017
rem_set->iterate_prts(*this);
9931018
}
1019+
}
1020+
1021+
virtual bool do_heap_region(HeapRegion* r) {
1022+
assert(r->in_collection_set(), "must be");
1023+
1024+
_scan_state->add_all_dirty_region(r->hrm_index());
1025+
dump_rem_set_for_region(r);
9941026

9951027
return false;
9961028
}
@@ -1022,7 +1054,7 @@ class G1MergeHeapRootsTask : public AbstractGangTask {
10221054
guarantee(r->rem_set()->occupancy_less_or_equal_than(G1RSetSparseRegionEntries),
10231055
"Found a not-small remembered set here. This is inconsistent with previous assumptions.");
10241056

1025-
_cl.do_heap_region(r);
1057+
_cl.dump_rem_set_for_region(r);
10261058

10271059
// We should only clear the card based remembered set here as we will not
10281060
// implicitly rebuild anything else during eager reclaim. Note that at the moment
@@ -1239,6 +1271,10 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) {
12391271
}
12401272
}
12411273

1274+
void G1RemSet::complete_evac_phase(bool has_more_than_one_evacuation_phase) {
1275+
_scan_state->complete_evac_phase(has_more_than_one_evacuation_phase);
1276+
}
1277+
12421278
void G1RemSet::exclude_region_from_scan(uint region_idx) {
12431279
_scan_state->clear_scan_top(region_idx);
12441280
}

src/hotspot/share/gc/g1/g1RemSet.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ class G1RemSet: public CHeapObj<mtGC> {
8484
void scan_heap_roots(G1ParScanThreadState* pss,
8585
uint worker_id,
8686
G1GCPhaseTimes::GCParPhases scan_phase,
87-
G1GCPhaseTimes::GCParPhases objcopy_phase);
87+
G1GCPhaseTimes::GCParPhases objcopy_phase,
88+
bool remember_already_scanned_cards);
8889

8990
// Merge cards from various sources (remembered sets, hot card cache, log buffers)
9091
// and calculate the cards that need to be scanned later (via scan_heap_roots()).
9192
// If initial_evacuation is set, this is called during the initial evacuation.
9293
void merge_heap_roots(bool initial_evacuation);
9394

95+
void complete_evac_phase(bool has_more_than_one_evacuation_phase);
9496
// Prepare for and cleanup after scanning the heap roots. Must be called
9597
// once before and after in sequential code.
9698
void prepare_for_scan_heap_roots();

0 commit comments

Comments
 (0)