8141637: Parallelize single threaded heap region iteration during Pre Evacuate Collection Set
Reviewed-by: tschatzl, lkorinth
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 27 11:31:46 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Nov 27 12:18:40 2019 +0100
@@ -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 @@
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 @@
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 @@
phase_times()->record_prepare_heap_roots_time_ms((Ticks::now() - start).seconds() * 1000.0);
}
- register_regions_with_region_attr();
+ {
+ 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
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Wed Nov 27 11:31:46 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Wed Nov 27 12:18:40 2019 +0100
@@ -593,6 +593,7 @@
// 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 @@
// 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) {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Wed Nov 27 11:31:46 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Wed Nov 27 12:18:40 2019 +0100
@@ -180,7 +180,7 @@
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 @@
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
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Nov 27 11:31:46 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Nov 27 12:18:40 2019 +0100
@@ -197,30 +197,6 @@
}
};
- // 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 @@
}
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 @@
} 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::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::prepare_for_scan_heap_roots(uint region_idx) {
+void G1RemSet::exclude_region_from_scan(uint region_idx) {
_scan_state->clear_scan_top(region_idx);
}
--- a/src/hotspot/share/gc/g1/g1RemSet.hpp Wed Nov 27 11:31:46 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1RemSet.hpp Wed Nov 27 12:18:40 2019 +0100
@@ -102,8 +102,11 @@
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.