8141637: Parallelize single threaded heap region iteration during Pre Evacuate Collection Set

Reviewed-by: tschatzl, lkorinth
This commit is contained in:
Stefan Johansson 2019-11-27 12:18:40 +01:00
parent 53263049c9
commit 4266daf9c9
5 changed files with 186 additions and 147 deletions

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -2777,112 +2777,6 @@ bool G1CollectedHeap::is_potential_eager_reclaim_candidate(HeapRegion* r) const
G1EagerReclaimHumongousObjects && rem_set->is_empty();
}
class RegisterRegionsWithRegionAttrTableClosure : public HeapRegionClosure {
private:
size_t _total_humongous;
size_t _candidate_humongous;
bool humongous_region_is_candidate(G1CollectedHeap* g1h, HeapRegion* region) const {
assert(region->is_starts_humongous(), "Must start a humongous object");
oop obj = oop(region->bottom());
// Dead objects cannot be eager reclaim candidates. Due to class
// unloading it is unsafe to query their classes so we return early.
if (g1h->is_obj_dead(obj, region)) {
return false;
}
// If we do not have a complete remembered set for the region, then we can
// not be sure that we have all references to it.
if (!region->rem_set()->is_complete()) {
return false;
}
// Candidate selection must satisfy the following constraints
// while concurrent marking is in progress:
//
// * In order to maintain SATB invariants, an object must not be
// reclaimed if it was allocated before the start of marking and
// has not had its references scanned. Such an object must have
// its references (including type metadata) scanned to ensure no
// live objects are missed by the marking process. Objects
// allocated after the start of concurrent marking don't need to
// be scanned.
//
// * An object must not be reclaimed if it is on the concurrent
// mark stack. Objects allocated after the start of concurrent
// marking are never pushed on the mark stack.
//
// Nominating only objects allocated after the start of concurrent
// marking is sufficient to meet both constraints. This may miss
// some objects that satisfy the constraints, but the marking data
// structures don't support efficiently performing the needed
// additional tests or scrubbing of the mark stack.
//
// However, we presently only nominate is_typeArray() objects.
// A humongous object containing references induces remembered
// set entries on other regions. In order to reclaim such an
// object, those remembered sets would need to be cleaned up.
//
// We also treat is_typeArray() objects specially, allowing them
// to be reclaimed even if allocated before the start of
// concurrent mark. For this we rely on mark stack insertion to
// exclude is_typeArray() objects, preventing reclaiming an object
// that is in the mark stack. We also rely on the metadata for
// such objects to be built-in and so ensured to be kept live.
// Frequent allocation and drop of large binary blobs is an
// important use case for eager reclaim, and this special handling
// may reduce needed headroom.
return obj->is_typeArray() &&
g1h->is_potential_eager_reclaim_candidate(region);
}
public:
RegisterRegionsWithRegionAttrTableClosure()
: _total_humongous(0),
_candidate_humongous(0) {
}
virtual bool do_heap_region(HeapRegion* r) {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
if (!r->is_starts_humongous()) {
g1h->register_region_with_region_attr(r);
return false;
}
bool is_candidate = humongous_region_is_candidate(g1h, r);
uint rindex = r->hrm_index();
g1h->set_humongous_reclaim_candidate(rindex, is_candidate);
if (is_candidate) {
g1h->register_humongous_region_with_region_attr(rindex);
_candidate_humongous++;
// We will later handle the remembered sets of these regions.
} else {
g1h->register_region_with_region_attr(r);
}
_total_humongous++;
return false;
}
size_t total_humongous() const { return _total_humongous; }
size_t candidate_humongous() const { return _candidate_humongous; }
};
void G1CollectedHeap::register_regions_with_region_attr() {
Ticks start = Ticks::now();
RegisterRegionsWithRegionAttrTableClosure cl;
heap_region_iterate(&cl);
phase_times()->record_register_regions((Ticks::now() - start).seconds() * 1000.0,
cl.total_humongous(),
cl.candidate_humongous());
_has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
}
#ifndef PRODUCT
void G1CollectedHeap::verify_region_attr_remset_update() {
class VerifyRegionAttrRemSet : public HeapRegionClosure {
@ -3699,6 +3593,145 @@ void G1CollectedHeap::merge_per_thread_state_info(G1ParScanThreadStateSet* per_t
phase_times()->record_or_add_time_secs(G1GCPhaseTimes::MergePSS, 0 /* worker_id */, (Ticks::now() - start).seconds());
}
class G1PrepareEvacuationTask : public AbstractGangTask {
class G1PrepareRegionsClosure : public HeapRegionClosure {
G1CollectedHeap* _g1h;
G1PrepareEvacuationTask* _parent_task;
size_t _worker_humongous_total;
size_t _worker_humongous_candidates;
bool humongous_region_is_candidate(HeapRegion* region) const {
assert(region->is_starts_humongous(), "Must start a humongous object");
oop obj = oop(region->bottom());
// Dead objects cannot be eager reclaim candidates. Due to class
// unloading it is unsafe to query their classes so we return early.
if (_g1h->is_obj_dead(obj, region)) {
return false;
}
// If we do not have a complete remembered set for the region, then we can
// not be sure that we have all references to it.
if (!region->rem_set()->is_complete()) {
return false;
}
// Candidate selection must satisfy the following constraints
// while concurrent marking is in progress:
//
// * In order to maintain SATB invariants, an object must not be
// reclaimed if it was allocated before the start of marking and
// has not had its references scanned. Such an object must have
// its references (including type metadata) scanned to ensure no
// live objects are missed by the marking process. Objects
// allocated after the start of concurrent marking don't need to
// be scanned.
//
// * An object must not be reclaimed if it is on the concurrent
// mark stack. Objects allocated after the start of concurrent
// marking are never pushed on the mark stack.
//
// Nominating only objects allocated after the start of concurrent
// marking is sufficient to meet both constraints. This may miss
// some objects that satisfy the constraints, but the marking data
// structures don't support efficiently performing the needed
// additional tests or scrubbing of the mark stack.
//
// However, we presently only nominate is_typeArray() objects.
// A humongous object containing references induces remembered
// set entries on other regions. In order to reclaim such an
// object, those remembered sets would need to be cleaned up.
//
// We also treat is_typeArray() objects specially, allowing them
// to be reclaimed even if allocated before the start of
// concurrent mark. For this we rely on mark stack insertion to
// exclude is_typeArray() objects, preventing reclaiming an object
// that is in the mark stack. We also rely on the metadata for
// such objects to be built-in and so ensured to be kept live.
// Frequent allocation and drop of large binary blobs is an
// important use case for eager reclaim, and this special handling
// may reduce needed headroom.
return obj->is_typeArray() &&
_g1h->is_potential_eager_reclaim_candidate(region);
}
public:
G1PrepareRegionsClosure(G1CollectedHeap* g1h, G1PrepareEvacuationTask* parent_task) :
_g1h(g1h),
_parent_task(parent_task),
_worker_humongous_total(0),
_worker_humongous_candidates(0) { }
~G1PrepareRegionsClosure() {
_parent_task->add_humongous_candidates(_worker_humongous_candidates);
_parent_task->add_humongous_total(_worker_humongous_total);
}
virtual bool do_heap_region(HeapRegion* hr) {
// First prepare the region for scanning
_g1h->rem_set()->prepare_region_for_scan(hr);
// Now check if region is a humongous candidate
if (!hr->is_starts_humongous()) {
_g1h->register_region_with_region_attr(hr);
return false;
}
uint index = hr->hrm_index();
if (humongous_region_is_candidate(hr)) {
_g1h->set_humongous_reclaim_candidate(index, true);
_g1h->register_humongous_region_with_region_attr(index);
_worker_humongous_candidates++;
// We will later handle the remembered sets of these regions.
} else {
_g1h->set_humongous_reclaim_candidate(index, false);
_g1h->register_region_with_region_attr(hr);
}
_worker_humongous_total++;
return false;
}
};
G1CollectedHeap* _g1h;
HeapRegionClaimer _claimer;
volatile size_t _humongous_total;
volatile size_t _humongous_candidates;
public:
G1PrepareEvacuationTask(G1CollectedHeap* g1h) :
AbstractGangTask("Prepare Evacuation"),
_g1h(g1h),
_claimer(_g1h->workers()->active_workers()),
_humongous_total(0),
_humongous_candidates(0) { }
~G1PrepareEvacuationTask() {
_g1h->set_has_humongous_reclaim_candidate(_humongous_candidates > 0);
}
void work(uint worker_id) {
G1PrepareRegionsClosure cl(_g1h, this);
_g1h->heap_region_par_iterate_from_worker_offset(&cl, &_claimer, worker_id);
}
void add_humongous_candidates(size_t candidates) {
Atomic::add(&_humongous_candidates, candidates);
}
void add_humongous_total(size_t total) {
Atomic::add(&_humongous_total, total);
}
size_t humongous_candidates() {
return _humongous_candidates;
}
size_t humongous_total() {
return _humongous_total;
}
};
void G1CollectedHeap::pre_evacuate_collection_set(G1EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* per_thread_states) {
_bytes_used_during_gc = 0;
@ -3718,9 +3751,16 @@ void G1CollectedHeap::pre_evacuate_collection_set(G1EvacuationInfo& evacuation_i
phase_times()->record_prepare_heap_roots_time_ms((Ticks::now() - start).seconds() * 1000.0);
}
register_regions_with_region_attr();
assert(_verifier->check_region_attr_table(), "Inconsistency in the region attributes table.");
{
G1PrepareEvacuationTask g1_prep_task(this);
Tickspan task_time = run_task(&g1_prep_task);
phase_times()->record_register_regions(task_time.seconds() * 1000.0,
g1_prep_task.humongous_total(),
g1_prep_task.humongous_candidates());
}
assert(_verifier->check_region_attr_table(), "Inconsistency in the region attributes table.");
_preserved_marks_set.assert_empty();
#if COMPILER2_OR_JVMCI

View File

@ -593,6 +593,7 @@ public:
// These are only valid for starts_humongous regions.
inline void set_humongous_reclaim_candidate(uint region, bool value);
inline bool is_humongous_reclaim_candidate(uint region);
inline void set_has_humongous_reclaim_candidate(bool value);
// Remove from the reclaim candidate set. Also remove from the
// collection set so that later encounters avoid the slow path.
@ -600,8 +601,7 @@ public:
// Register the given region to be part of the collection set.
inline void register_humongous_region_with_region_attr(uint index);
// Update region attributes table with information about all regions.
void register_regions_with_region_attr();
// We register a region with the fast "in collection set" test. We
// simply set to true the array slot corresponding to this region.
void register_young_region_with_region_attr(HeapRegion* r) {

View File

@ -180,7 +180,7 @@ void G1CollectedHeap::register_region_with_region_attr(HeapRegion* r) {
void G1CollectedHeap::register_old_region_with_region_attr(HeapRegion* r) {
_region_attr.set_in_old(r->hrm_index(), r->rem_set()->is_tracked());
_rem_set->prepare_for_scan_heap_roots(r->hrm_index());
_rem_set->exclude_region_from_scan(r->hrm_index());
}
void G1CollectedHeap::register_optional_region_with_region_attr(HeapRegion* r) {
@ -298,6 +298,10 @@ inline bool G1CollectedHeap::is_humongous_reclaim_candidate(uint region) {
return _humongous_reclaim_candidates.is_candidate(region);
}
inline void G1CollectedHeap::set_has_humongous_reclaim_candidate(bool value) {
_has_humongous_reclaim_candidates = value;
}
inline void G1CollectedHeap::set_humongous_is_live(oop obj) {
uint region = addr_to_region((HeapWord*)obj);
// Clear the flag in the humongous_reclaim_candidates table. Also

View File

@ -197,30 +197,6 @@ private:
}
};
// Creates a snapshot of the current _top values at the start of collection to
// filter out card marks that we do not want to scan.
class G1ResetScanTopClosure : public HeapRegionClosure {
G1RemSetScanState* _scan_state;
public:
G1ResetScanTopClosure(G1RemSetScanState* scan_state) : _scan_state(scan_state) { }
virtual bool do_heap_region(HeapRegion* r) {
uint hrm_index = r->hrm_index();
if (r->in_collection_set()) {
// Young regions had their card table marked as young at their allocation;
// we need to make sure that these marks are cleared at the end of GC, *but*
// they should not be scanned for cards.
// So directly add them to the "all_dirty_regions".
// Same for regions in the (initial) collection set: they may contain cards from
// the log buffers, make sure they are cleaned.
_scan_state->add_all_dirty_region(hrm_index);
} else if (r->is_old_or_humongous_or_archive()) {
_scan_state->set_scan_top(hrm_index, r->top());
}
return false;
}
};
// For each region, contains the maximum top() value to be used during this garbage
// collection. Subsumes common checks like filtering out everything but old and
// humongous regions outside the collection set.
@ -329,16 +305,8 @@ public:
}
void prepare() {
for (size_t i = 0; i < _max_regions; i++) {
_collection_set_iter_state[i] = false;
clear_scan_top((uint)i);
}
_all_dirty_regions = new G1DirtyRegions(_max_regions);
_next_dirty_regions = new G1DirtyRegions(_max_regions);
G1ResetScanTopClosure cl(this);
G1CollectedHeap::heap()->heap_region_iterate(&cl);
}
void prepare_for_merge_heap_roots() {
@ -431,6 +399,10 @@ public:
} while (cur != start_pos);
}
void reset_region_claim(uint region_idx) {
_collection_set_iter_state[region_idx] = false;
}
// Attempt to claim the given region in the collection set for iteration. Returns true
// if this call caused the transition from Unclaimed to Claimed.
inline bool claim_collection_set_region(uint region) {
@ -910,6 +882,26 @@ void G1RemSet::scan_collection_set_regions(G1ParScanThreadState* pss,
}
}
void G1RemSet::prepare_region_for_scan(HeapRegion* region) {
uint hrm_index = region->hrm_index();
_scan_state->reset_region_claim(hrm_index);
if (region->in_collection_set()) {
// Young regions had their card table marked as young at their allocation;
// we need to make sure that these marks are cleared at the end of GC, *but*
// they should not be scanned for cards.
// So directly add them to the "all_dirty_regions".
// Same for regions in the (initial) collection set: they may contain cards from
// the log buffers, make sure they are cleaned.
_scan_state->clear_scan_top(hrm_index);
_scan_state->add_all_dirty_region(hrm_index);
} else if (region->is_old_or_humongous_or_archive()) {
_scan_state->set_scan_top(hrm_index, region->top());
} else {
assert(region->is_free(), "Should only be free region at this point %s", region->get_type_str());
}
}
void G1RemSet::prepare_for_scan_heap_roots() {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
dcqs.concatenate_logs();
@ -1237,7 +1229,7 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) {
}
}
void G1RemSet::prepare_for_scan_heap_roots(uint region_idx) {
void G1RemSet::exclude_region_from_scan(uint region_idx) {
_scan_state->clear_scan_top(region_idx);
}

View File

@ -102,8 +102,11 @@ public:
void prepare_for_scan_heap_roots();
// Cleans the card table from temporary duplicate detection information.
void cleanup_after_scan_heap_roots();
// Prepares the given region for heap root scanning.
void prepare_for_scan_heap_roots(uint region_idx);
// Excludes the given region from heap root scanning.
void exclude_region_from_scan(uint region_idx);
// Creates a snapshot of the current _top values at the start of collection to
// filter out card marks that we do not want to scan.
void prepare_region_for_scan(HeapRegion* region);
// Do work for regions in the current increment of the collection set, scanning
// non-card based (heap) roots.