Skip to content

Commit 6879c44

Browse files
author
Ivan Walulya
committed
8351405: G1: Collection set early pruning causes suboptimal region selection
Reviewed-by: ayang, tschatzl
1 parent aee4d69 commit 6879c44

13 files changed

+75
-20
lines changed

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ int G1CSetCandidateGroup::compare_gc_efficiency(G1CSetCandidateGroup** gr1, G1CS
134134
}
135135
}
136136

137-
int G1CSetCandidateGroup::compare_reclaimble_bytes(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) {
137+
int G1CollectionSetCandidateInfo::compare_region_gc_efficiency(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) {
138138
// Make sure that null entries are moved to the end.
139139
if (ci1->_r == nullptr) {
140140
if (ci2->_r == nullptr) {
@@ -146,12 +146,13 @@ int G1CSetCandidateGroup::compare_reclaimble_bytes(G1CollectionSetCandidateInfo*
146146
return -1;
147147
}
148148

149-
size_t reclaimable1 = ci1->_r->reclaimable_bytes();
150-
size_t reclaimable2 = ci2->_r->reclaimable_bytes();
149+
G1Policy* p = G1CollectedHeap::heap()->policy();
150+
double gc_efficiency1 = p->predict_gc_efficiency(ci1->_r);
151+
double gc_efficiency2 = p->predict_gc_efficiency(ci2->_r);
151152

152-
if (reclaimable1 > reclaimable2) {
153+
if (gc_efficiency1 > gc_efficiency2) {
153154
return -1;
154-
} else if (reclaimable1 < reclaimable2) {
155+
} else if (gc_efficiency1 < gc_efficiency2) {
155156
return 1;
156157
} else {
157158
return 0;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ struct G1CollectionSetCandidateInfo {
4848
++_num_unreclaimed;
4949
return _num_unreclaimed < G1NumCollectionsKeepPinned;
5050
}
51+
52+
static int compare_region_gc_efficiency(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2);
5153
};
5254

5355
using G1CSetCandidateGroupIterator = GrowableArrayIterator<G1CollectionSetCandidateInfo>;
@@ -106,8 +108,6 @@ class G1CSetCandidateGroup : public CHeapObj<mtGCCardSet>{
106108
// up at the end of the list.
107109
static int compare_gc_efficiency(G1CSetCandidateGroup** gr1, G1CSetCandidateGroup** gr2);
108110

109-
static int compare_reclaimble_bytes(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2);
110-
111111
double gc_efficiency() const { return _gc_efficiency; }
112112

113113
G1HeapRegion* region_at(uint i) const { return _candidates.at(i)._r; }

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ class G1BuildCandidateRegionsTask : public WorkerTask {
9696
_data[idx] = CandidateInfo(hr);
9797
}
9898

99-
void sort_by_reclaimable_bytes() {
99+
void sort_by_gc_efficiency() {
100100
if (_cur_claim_idx == 0) {
101101
return;
102102
}
103103
for (uint i = _cur_claim_idx; i < _max_size; i++) {
104104
assert(_data[i]._r == nullptr, "must be");
105105
}
106-
qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)G1CSetCandidateGroup::compare_reclaimble_bytes);
106+
qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)G1CollectionSetCandidateInfo::compare_region_gc_efficiency);
107107
for (uint i = _cur_claim_idx; i < _max_size; i++) {
108108
assert(_data[i]._r == nullptr, "must be");
109109
}
@@ -247,7 +247,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask {
247247
}
248248

249249
void sort_and_prune_into(G1CollectionSetCandidates* candidates) {
250-
_result.sort_by_reclaimable_bytes();
250+
_result.sort_by_gc_efficiency();
251251
prune(_result.array());
252252
candidates->set_candidates_from_marking(_result.array(),
253253
_num_regions_added);

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,8 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask {
12881288
reclaim_empty_humongous_region(hr);
12891289
}
12901290
} else if (hr->is_old()) {
1291-
hr->note_end_of_marking(_cm->top_at_mark_start(hr), _cm->live_bytes(hr->hrm_index()));
1291+
uint region_idx = hr->hrm_index();
1292+
hr->note_end_of_marking(_cm->top_at_mark_start(hr), _cm->live_bytes(region_idx), _cm->incoming_refs(region_idx));
12921293

12931294
if (hr->live_bytes() != 0) {
12941295
if (tracker->update_old_before_rebuild(hr)) {

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

+4
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,8 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
562562
size_t live_bytes(uint region) const { return _region_mark_stats[region]._live_words * HeapWordSize; }
563563
// Set live bytes for concurrent marking.
564564
void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words = live_bytes / HeapWordSize; }
565+
// Approximate number of incoming references found during marking.
566+
size_t incoming_refs(uint region) const { return _region_mark_stats[region]._incoming_refs; }
565567

566568
// Update the TAMS for the given region to the current top.
567569
inline void update_top_at_mark_start(G1HeapRegion* r);
@@ -952,6 +954,8 @@ class G1CMTask : public TerminatorTerminator {
952954

953955
inline void update_liveness(oop const obj, size_t const obj_size);
954956

957+
inline void inc_incoming_refs(oop const obj);
958+
955959
// Clear (without flushing) the mark cache entry for the given region.
956960
void clear_mark_stats_cache(uint region_idx);
957961
// Evict the whole statistics cache into the global statistics. Returns the

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

+8
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ inline void G1CMTask::update_liveness(oop const obj, const size_t obj_size) {
228228
_mark_stats_cache.add_live_words(_g1h->addr_to_region(obj), obj_size);
229229
}
230230

231+
inline void G1CMTask::inc_incoming_refs(oop const obj) {
232+
_mark_stats_cache.inc_incoming_refs(_g1h->addr_to_region(obj));
233+
}
234+
231235
inline void G1ConcurrentMark::add_to_liveness(uint worker_id, oop const obj, size_t size) {
232236
task(worker_id)->update_liveness(obj, size);
233237
}
@@ -288,6 +292,10 @@ inline bool G1CMTask::deal_with_reference(T* p) {
288292
if (obj == nullptr) {
289293
return false;
290294
}
295+
296+
if (!G1HeapRegion::is_in_same_region(p, obj)) {
297+
inc_incoming_refs(obj);
298+
}
291299
return make_reference_grey(obj);
292300
}
293301

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

+3
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ void G1HeapRegion::hr_clear(bool clear_space) {
128128

129129
_parsable_bottom = bottom();
130130
_garbage_bytes = 0;
131+
_incoming_refs = 0;
131132

132133
if (clear_space) clear(SpaceDecorator::Mangle);
133134
}
@@ -239,6 +240,7 @@ G1HeapRegion::G1HeapRegion(uint hrm_index,
239240
#endif
240241
_parsable_bottom(nullptr),
241242
_garbage_bytes(0),
243+
_incoming_refs(0),
242244
_young_index_in_cset(-1),
243245
_surv_rate_group(nullptr),
244246
_age_index(G1SurvRateGroup::InvalidAgeIndex),
@@ -278,6 +280,7 @@ void G1HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) {
278280
assert(parsable_bottom_acquire() == bottom(), "must be");
279281

280282
_garbage_bytes = 0;
283+
_incoming_refs = 0;
281284
}
282285

283286
void G1HeapRegion::note_self_forward_chunk_done(size_t garbage_bytes) {

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

+9-3
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ class G1HeapRegion : public CHeapObj<mtGC> {
237237
// Amount of dead data in the region.
238238
size_t _garbage_bytes;
239239

240+
// Approximate number of references to this regions at the end of concurrent
241+
// marking. We we do not mark through all objects, so this is an estimate.
242+
size_t _incoming_refs;
243+
240244
// Data for young region survivor prediction.
241245
uint _young_index_in_cset;
242246
G1SurvRateGroup* _surv_rate_group;
@@ -339,6 +343,8 @@ class G1HeapRegion : public CHeapObj<mtGC> {
339343
return capacity() - known_live_bytes;
340344
}
341345

346+
size_t incoming_refs() { return _incoming_refs; }
347+
342348
inline bool is_collection_set_candidate() const;
343349

344350
// Retrieve parsable bottom; since it may be modified concurrently, outside a
@@ -351,9 +357,9 @@ class G1HeapRegion : public CHeapObj<mtGC> {
351357
// that the collector is about to start or has finished (concurrently)
352358
// marking the heap.
353359

354-
// Notify the region that concurrent marking has finished. Passes TAMS and the number of
355-
// bytes marked between bottom and TAMS.
356-
inline void note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes);
360+
// Notify the region that concurrent marking has finished. Passes TAMS, the number of
361+
// bytes marked between bottom and TAMS, and the estimate for incoming references.
362+
inline void note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes, size_t incoming_refs);
357363

358364
// Notify the region that scrubbing has completed.
359365
inline void note_end_of_scrubbing();

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ inline void G1HeapRegion::reset_after_full_gc_common() {
156156

157157
_garbage_bytes = 0;
158158

159+
_incoming_refs = 0;
160+
159161
// Clear unused heap memory in debug builds.
160162
if (ZapUnusedHeapArea) {
161163
mangle_unused_area();
@@ -263,11 +265,12 @@ inline void G1HeapRegion::reset_parsable_bottom() {
263265
Atomic::release_store(&_parsable_bottom, bottom());
264266
}
265267

266-
inline void G1HeapRegion::note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes) {
268+
inline void G1HeapRegion::note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes, size_t incoming_refs) {
267269
assert_at_safepoint();
268270

269271
if (top_at_mark_start != bottom()) {
270272
_garbage_bytes = byte_size(bottom(), top_at_mark_start) - marked_bytes;
273+
_incoming_refs = incoming_refs;
271274
}
272275

273276
if (needs_scrubbing()) {

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

+9
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,15 @@ size_t G1Policy::predict_bytes_to_copy(G1HeapRegion* hr) const {
11071107
return bytes_to_copy;
11081108
}
11091109

1110+
double G1Policy::predict_gc_efficiency(G1HeapRegion* hr) {
1111+
1112+
double total_based_on_incoming_refs_ms = predict_merge_scan_time(hr->incoming_refs()) + // We use the number of incoming references as an estimate for remset cards.
1113+
predict_non_young_other_time_ms(1) +
1114+
predict_region_copy_time_ms(hr, false /* for_young_only_phase */);
1115+
1116+
return hr->reclaimable_bytes() / total_based_on_incoming_refs_ms;
1117+
}
1118+
11101119
double G1Policy::predict_young_region_other_time_ms(uint count) const {
11111120
return _analytics->predict_young_other_time_ms(count);
11121121
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ class G1Policy: public CHeapObj<mtGC> {
243243
size_t predict_bytes_to_copy(G1HeapRegion* hr) const;
244244
size_t pending_cards_at_gc_start() const { return _pending_cards_at_gc_start; }
245245

246+
// GC efficiency for collecting the region based on the time estimate for
247+
// merging and scanning incoming references.
248+
double predict_gc_efficiency(G1HeapRegion* hr);
249+
246250
// The minimum number of retained regions we will add to the CSet during a young GC.
247251
uint min_retained_old_cset_length() const;
248252
// Calculate the minimum number of old regions we'll add to the CSet

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

+16-5
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,26 @@
3333

3434
// Per-Region statistics gathered during marking.
3535
//
36-
// This includes
36+
// These include:
3737
// * the number of live words gathered during marking for the area from bottom
38-
// to tams. This is an exact measure.
39-
// The code corrects later for the live data between tams and top.
38+
// to tams. This is an exact measure. The code corrects later for the live data
39+
// between tams and top.
40+
// * the number of incoming references found during marking. This is an approximate
41+
// value because we do not mark through all objects.
4042
struct G1RegionMarkStats {
4143
size_t _live_words;
44+
size_t _incoming_refs;
4245

4346
// Clear all members.
4447
void clear() {
4548
_live_words = 0;
49+
_incoming_refs = 0;
4650
}
47-
// Clear all members after a marking overflow. Nothing to do as the live words
48-
// are updated by the atomic mark. We do not remark objects after overflow.
51+
// Clear all members after a marking overflow. Only needs to clear the number of
52+
// incoming references as all objects will be rescanned, while the live words are
53+
// gathered whenever a thread can mark an object, which is synchronized.
4954
void clear_during_overflow() {
55+
_incoming_refs = 0;
5056
}
5157
};
5258

@@ -107,6 +113,11 @@ class G1RegionMarkStatsCache {
107113
cur->_stats._live_words += live_words;
108114
}
109115

116+
void inc_incoming_refs(uint region_idx) {
117+
G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx);
118+
cur->_stats._incoming_refs++;
119+
}
120+
110121
void reset(uint region_idx) {
111122
uint const cache_idx = hash(region_idx);
112123
G1RegionMarkStatsCacheEntry* cur = &_cache[cache_idx];

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

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ inline void G1RegionMarkStatsCache::evict(uint idx) {
4949
if (cur->_stats._live_words != 0) {
5050
Atomic::add(&_target[cur->_region_idx]._live_words, cur->_stats._live_words);
5151
}
52+
53+
if (cur->_stats._incoming_refs != 0) {
54+
Atomic::add(&_target[cur->_region_idx]._incoming_refs, cur->_stats._incoming_refs);
55+
}
56+
5257
cur->clear();
5358
}
5459

0 commit comments

Comments
 (0)