8141637: Parallelize single threaded heap region iteration during Pre Evacuate Collection Set
Reviewed-by: tschatzl, lkorinth
This commit is contained in:
parent
53263049c9
commit
4266daf9c9
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user