diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 57b7f91260c..5286a764996 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -779,6 +779,15 @@ G1AddCardResult G1CardSet::add_card(uintptr_t card) { uint card_within_region; split_card(card, card_region, card_within_region); +#ifdef ASSERT + { + uint region_idx = card_region >> config()->log2_card_regions_per_heap_region(); + G1HeapRegion* r = G1CollectedHeap::heap()->region_at(region_idx); + assert(!r->rem_set()->is_added_to_cset_group() || + r->rem_set()->cset_group()->card_set() != this, "Should not be sharing a cardset"); + } +#endif + return add_card(card_region, card_within_region, true /* increment_total */); } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 478c8f48f27..3f84349b90b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1159,8 +1159,7 @@ G1CollectedHeap::G1CollectedHeap() : _rem_set(nullptr), _card_set_config(), _card_set_freelist_pool(G1CardSetConfiguration::num_mem_object_types()), - _young_regions_cardset_mm(card_set_config(), card_set_freelist_pool()), - _young_regions_cardset(card_set_config(), &_young_regions_cardset_mm), + _young_regions_cset_group(card_set_config(), &_card_set_freelist_pool, 1u /* group_id */), _cm(nullptr), _cm_thread(nullptr), _cr(nullptr), @@ -2716,7 +2715,7 @@ bool G1CollectedHeap::is_old_gc_alloc_region(G1HeapRegion* hr) { void G1CollectedHeap::set_region_short_lived_locked(G1HeapRegion* hr) { _eden.add(hr); _policy->set_region_eden(hr); - hr->install_group_cardset(young_regions_cardset()); + young_regions_cset_group()->add(hr); } #ifdef ASSERT @@ -2927,7 +2926,7 @@ G1HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegio _survivor.add(new_alloc_region); register_new_survivor_region_with_region_attr(new_alloc_region); // Install the group cardset. - new_alloc_region->install_group_cardset(young_regions_cardset()); + young_regions_cset_group()->add(new_alloc_region); } else { new_alloc_region->set_old(); } @@ -3070,6 +3069,8 @@ void G1CollectedHeap::finish_codecache_marking_cycle() { CodeCache::arm_all_nmethods(); } -void G1CollectedHeap::prepare_group_cardsets_for_scan () { - _young_regions_cardset.reset_table_scanner_for_groups(); +void G1CollectedHeap::prepare_group_cardsets_for_scan() { + young_regions_cardset()->reset_table_scanner_for_groups(); + + collection_set()->prepare_groups_for_scan(); } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 73dfc85bb1b..16d73537d5c 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -781,15 +781,15 @@ private: G1MonotonicArenaFreePool _card_set_freelist_pool; // Group cardsets - G1CardSetMemoryManager _young_regions_cardset_mm; - G1CardSet _young_regions_cardset; + G1CSetCandidateGroup _young_regions_cset_group; public: G1CardSetConfiguration* card_set_config() { return &_card_set_config; } - G1CardSet* young_regions_cardset() { return &_young_regions_cardset; }; + G1CSetCandidateGroup* young_regions_cset_group() { return &_young_regions_cset_group; } + G1CardSet* young_regions_cardset() { return _young_regions_cset_group.card_set(); }; - G1CardSetMemoryManager* young_regions_card_set_mm() { return &_young_regions_cardset_mm; } + G1MonotonicArenaMemoryStats young_regions_card_set_memory_stats() { return _young_regions_cset_group.card_set_memory_stats(); } void prepare_group_cardsets_for_scan(); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 63eceb4a5ba..943c68b74c7 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -225,7 +225,6 @@ void G1CollectedHeap::register_region_with_region_attr(G1HeapRegion* r) { } void G1CollectedHeap::register_old_region_with_region_attr(G1HeapRegion* r) { - assert(!r->has_pinned_objects(), "must be"); assert(r->rem_set()->is_complete(), "must be"); _region_attr.set_in_old(r->hrm_index(), true); _rem_set->exclude_region_from_scan(r->hrm_index()); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 91c13183c4b..19056245152 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -24,7 +24,7 @@ #include "gc/g1/g1Analytics.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSet.hpp" +#include "gc/g1/g1CollectionSet.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" @@ -53,10 +53,13 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : _collection_set_regions(nullptr), _collection_set_cur_length(0), _collection_set_max_length(0), + _collection_set_groups(), + _selected_groups_cur_length(0), + _selected_groups_inc_part_start(0), _eden_region_length(0), _survivor_region_length(0), _initial_old_region_length(0), - _optional_old_regions(), + _optional_groups(), _inc_build_state(Inactive), _inc_part_start(0) { } @@ -77,7 +80,8 @@ void G1CollectionSet::init_region_lengths(uint eden_cset_region_length, "Young region length %u should match collection set length %u", young_region_length(), _collection_set_cur_length); _initial_old_region_length = 0; - _optional_old_regions.clear(); + assert(_optional_groups.length() == 0, "Should not have any optional groups yet"); + _optional_groups.clear(); } void G1CollectionSet::initialize(uint max_region_length) { @@ -91,7 +95,10 @@ void G1CollectionSet::initialize(uint max_region_length) { void G1CollectionSet::abandon_all_candidates() { _candidates.clear(); _initial_old_region_length = 0; - _optional_old_regions.clear(); +} + +void G1CollectionSet::prepare_groups_for_scan () { + collection_set_groups()->prepare_for_scan(); } void G1CollectionSet::add_old_region(G1HeapRegion* hr) { @@ -101,6 +108,8 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { "Precondition, actively building cset or adding optional later on"); assert(hr->is_old(), "the region should be old"); + assert(!hr->rem_set()->is_added_to_cset_group(), "Should have already uninstalled group remset"); + assert(!hr->in_collection_set(), "should not already be in the collection set"); _g1h->register_old_region_with_region_attr(hr); @@ -126,6 +135,7 @@ void G1CollectionSet::finalize_incremental_building() { void G1CollectionSet::clear() { assert_at_safepoint_on_vm_thread(); _collection_set_cur_length = 0; + _collection_set_groups.clear(); } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { @@ -151,10 +161,10 @@ void G1CollectionSet::par_iterate(G1HeapRegionClosure* cl, void G1CollectionSet::iterate_optional(G1HeapRegionClosure* cl) const { assert_at_safepoint(); - for (G1HeapRegion* r : _optional_old_regions) { + _optional_groups.iterate([&] (G1HeapRegion* r) { bool result = cl->do_heap_region(r); guarantee(!result, "Must not cancel iteration"); - } + }); } void G1CollectionSet::iterate_incremental_part_from(G1HeapRegionClosure* cl, @@ -320,59 +330,40 @@ static int compare_region_idx(const uint a, const uint b) { return static_cast(a-b); } -// The current mechanism skips evacuation of pinned old regions like g1 does for -// young regions: -// * evacuating pinned marking collection set candidate regions (available during mixed -// gc) like young regions would not result in any memory gain but only take additional -// time away from processing regions that would actually result in memory being freed. -// To advance mixed gc progress (we committed to evacuate all marking collection set -// candidate regions within the maximum number of mixed gcs in the phase), move them -// to the optional collection set candidates to reclaim them asap as time permits. -// * evacuating out retained collection set candidates would also just take up time with -// no actual space freed in old gen. Better to concentrate on others. -// Retained collection set candidates are aged out, ie. made to regular old regions -// without remembered sets after a few attempts to save computation costs of keeping -// them candidates for very long living pinned regions. +// The current mechanism for evacuating pinned old regions is as below: +// * pinned regions in the marking collection set candidate list (available during mixed gc) are evacuated like +// pinned young regions to avoid the complexity of dealing with pinned regions that are part of a +// collection group sharing a single cardset. These regions will be partially evacuated and added to the +// retained collection set by the evacuation failure handling mechanism. +// * evacuating pinned regions out of retained collection set candidates would also just take up time +// with no actual space freed in old gen. Better to concentrate on others. So we skip over pinned +// regions in retained collection set candidates. Retained collection set candidates are aged out, ie. +// made to regular old regions without remembered sets after a few attempts to save computation costs +// of keeping them candidates for very long living pinned regions. void G1CollectionSet::finalize_old_part(double time_remaining_ms) { double non_young_start_time_sec = os::elapsedTime(); + _selected_groups_cur_length = 0; + _selected_groups_inc_part_start = 0; + if (!candidates()->is_empty()) { candidates()->verify(); - G1CollectionCandidateRegionList initial_old_regions; - assert(_optional_old_regions.length() == 0, "must be"); - G1CollectionCandidateRegionList pinned_marking_regions; - G1CollectionCandidateRegionList pinned_retained_regions; - if (collector_state()->in_mixed_phase()) { - time_remaining_ms = select_candidates_from_marking(time_remaining_ms, - &initial_old_regions, - &pinned_marking_regions); + time_remaining_ms = select_candidates_from_marking(time_remaining_ms); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); } - select_candidates_from_retained(time_remaining_ms, - &initial_old_regions, - &pinned_retained_regions); - - // Move initially selected old regions to collection set directly. - move_candidates_to_collection_set(&initial_old_regions); - // Only prepare selected optional regions for now. - prepare_optional_regions(&_optional_old_regions); - // Move pinned marking regions we came across to retained candidates so that - // there is progress in the mixed gc phase. - move_pinned_marking_to_retained(&pinned_marking_regions); - // Drop pinned retained regions to make progress with retained regions. Regions - // in that list must have been pinned for at least G1NumCollectionsKeepPinned - // GCs and hence are considered "long lived". - drop_pinned_retained_regions(&pinned_retained_regions); - + if (candidates()->retained_groups().num_regions() > 0) { + select_candidates_from_retained(time_remaining_ms); + } candidates()->verify(); } else { log_debug(gc, ergo, cset)("No candidates to reclaim."); } + _selected_groups_cur_length = collection_set_groups()->length(); stop_incremental_building(); double non_young_end_time_sec = os::elapsedTime(); @@ -381,75 +372,60 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { QuickSort::sort(_collection_set_regions, _collection_set_cur_length, compare_region_idx); } -void G1CollectionSet::move_candidates_to_collection_set(G1CollectionCandidateRegionList* regions) { - for (G1HeapRegion* r : *regions) { - _g1h->clear_region_attr(r); - add_old_region(r); - } - candidates()->remove(regions); -} - static void print_finish_message(const char* reason, bool from_marking) { log_debug(gc, ergo, cset)("Finish adding %s candidates to collection set (%s).", from_marking ? "marking" : "retained", reason); } -double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { +double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) { uint num_expensive_regions = 0; - - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_pinned_regions = 0; + uint num_inital_regions = 0; + uint num_initial_groups = 0; + uint num_optional_regions = 0; double predicted_initial_time_ms = 0.0; double predicted_optional_time_ms = 0.0; double optional_threshold_ms = time_remaining_ms * _policy->optional_prediction_fraction(); - const uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); - const uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); - const uint max_optional_regions = max_old_cset_length - min_old_cset_length; + uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); + uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); bool check_time_remaining = _policy->use_adaptive_young_list_length(); - G1CollectionCandidateList* marking_list = &candidates()->marking_regions(); - assert(marking_list != nullptr, "must be"); + G1CSetCandidateGroupList* from_marking_groups = &candidates()->from_marking_groups(); log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " - "Min %u regions, max %u regions, available %u regions" + "Min %u regions, max %u regions, available %u regions (%u groups), " "time remaining %1.2fms, optional threshold %1.2fms", - min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); + min_old_cset_length, max_old_cset_length, from_marking_groups->num_regions(), from_marking_groups->length(), + time_remaining_ms, optional_threshold_ms); - G1CollectionCandidateListIterator iter = marking_list->begin(); - for (; iter != marking_list->end(); ++iter) { - if (num_initial_regions_selected + num_optional_regions_selected >= max_old_cset_length) { + G1CSetCandidateGroupList selected_groups; + + for (G1CSetCandidateGroup* group : *from_marking_groups) { + if (num_inital_regions + num_optional_regions >= max_old_cset_length) { // Added maximum number of old regions to the CSet. print_finish_message("Maximum number of regions reached", true); break; } - G1HeapRegion* hr = (*iter)->_r; - // Skip evacuating pinned marking regions because we are not getting any free - // space from them (and we expect to get free space from marking candidates). - // Also prepare to move them to retained regions to be evacuated optionally later - // to not impact the mixed phase too much. - if (hr->has_pinned_objects()) { - num_pinned_regions++; - (*iter)->update_num_unreclaimed(); - log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); - pinned_old_regions->append(hr); - continue; - } - double predicted_time_ms = _policy->predict_region_total_time_ms(hr, false); + + double predicted_time_ms = group->predict_group_total_time_ms(); + time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); // Add regions to old set until we reach the minimum amount - if (initial_old_regions->length() < min_old_cset_length) { - initial_old_regions->append(hr); - num_initial_regions_selected++; + if (num_inital_regions < min_old_cset_length) { + + num_initial_groups++; + + add_group_to_collection_set(group); + selected_groups.append(group); + + num_inital_regions += group->length(); + predicted_initial_time_ms += predicted_time_ms; // Record the number of regions added with no time remaining if (time_remaining_ms == 0.0) { - num_expensive_regions++; + num_expensive_regions += group->length(); } } else if (!check_time_remaining) { // In the non-auto-tuning case, we'll finish adding regions @@ -459,22 +435,34 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms, } else { // Keep adding regions to old set until we reach the optional threshold if (time_remaining_ms > optional_threshold_ms) { + num_initial_groups++; + + add_group_to_collection_set(group); + selected_groups.append(group); + + num_inital_regions += group->length(); + predicted_initial_time_ms += predicted_time_ms; - initial_old_regions->append(hr); - num_initial_regions_selected++; + } else if (time_remaining_ms > 0) { // Keep adding optional regions until time is up. - assert(_optional_old_regions.length() < max_optional_regions, "Should not be possible."); + _optional_groups.append(group); + prepare_optional_group(group, num_optional_regions); + num_optional_regions += group->length(); predicted_optional_time_ms += predicted_time_ms; - _optional_old_regions.append(hr); - num_optional_regions_selected++; } else { print_finish_message("Predicted time too high", true); break; } } } - if (iter == marking_list->end()) { + + // Remove selected groups from list of candidate groups. + if (num_initial_groups > 0) { + candidates()->remove(&selected_groups); + } + + if (from_marking_groups->length() == 0) { log_debug(gc, ergo, cset)("Marking candidates exhausted."); } @@ -483,22 +471,20 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms, num_expensive_regions); } - log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " + log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u regions (%u groups), optional: %u regions (%u groups), " "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + selected_groups.num_regions(), selected_groups.length(), _optional_groups.num_regions(), _optional_groups.length(), predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); - assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); - assert(_optional_old_regions.length() == num_optional_regions_selected, "must be"); + assert(selected_groups.num_regions() == num_inital_regions, "must be"); + assert(_optional_groups.num_regions() == num_optional_regions, "must be"); return time_remaining_ms; } -void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_expensive_regions_selected = 0; +void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms) { + uint num_initial_regions = 0; + uint num_optional_regions = 0; + uint num_expensive_regions = 0; uint num_pinned_regions = 0; double predicted_initial_time_ms = 0.0; @@ -513,17 +499,27 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, double optional_time_remaining_ms = _policy->max_time_for_retaining(); time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); - G1CollectionCandidateList* retained_list = &candidates()->retained_regions(); + G1CSetCandidateGroupList* retained_groups = &candidates()->retained_groups(); log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " - "Min %u regions, available %u, " + "Min %u regions, available %u regions (%u groups), " "time remaining %1.2fms, optional remaining %1.2fms", - min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); + min_regions, retained_groups->num_regions(), retained_groups->length(), + time_remaining_ms, optional_time_remaining_ms); + + G1CSetCandidateGroupList remove_from_retained; + G1CSetCandidateGroupList groups_to_abandon; + + for (G1CSetCandidateGroup* group : *retained_groups) { + assert(group->length() == 1, "Retained groups should have only 1 region"); + + double predicted_time_ms = group->predict_group_total_time_ms(); - for (G1CollectionSetCandidateInfo* ci : *retained_list) { - G1HeapRegion* r = ci->_r; - double predicted_time_ms = _policy->predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; + + G1CollectionSetCandidateInfo* ci = group->at(0); // We only have one region in the group. + G1HeapRegion* r = ci->_r; + // If we can't reclaim that region ignore it for now. if (r->has_pinned_objects()) { num_pinned_regions++; @@ -531,22 +527,32 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); } else { log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); - pinned_old_regions->append(r); + // Drop pinned retained regions to make progress with retained regions. Regions + // in that list must have been pinned for at least G1NumCollectionsKeepPinned + // GCs and hence are considered "long lived". + _g1h->clear_region_attr(r); + groups_to_abandon.append(group); + remove_from_retained.append(group); } continue; } - if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { + if (fits_in_remaining_time || (num_expensive_regions < min_regions)) { predicted_initial_time_ms += predicted_time_ms; if (!fits_in_remaining_time) { - num_expensive_regions_selected++; + num_expensive_regions++; } - initial_old_regions->append(r); - num_initial_regions_selected++; + + add_group_to_collection_set(group); + remove_from_retained.append(group); + + num_initial_regions += group->length(); } else if (predicted_time_ms <= optional_time_remaining_ms) { + // Prepare optional collection region. + _optional_groups.append(group); + prepare_optional_group(group, num_optional_regions); + num_optional_regions += group->length(); predicted_optional_time_ms += predicted_time_ms; - _optional_old_regions.append(r); - num_optional_regions_selected++; } else { // Fits neither initial nor optional time limit. Exit. break; @@ -555,85 +561,106 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, optional_time_remaining_ms = MAX2(0.0, optional_time_remaining_ms - predicted_time_ms); } - uint num_regions_selected = num_initial_regions_selected + num_optional_regions_selected; - if (num_regions_selected == retained_list->length()) { + if (num_initial_regions == retained_groups->num_regions()) { log_debug(gc, ergo, cset)("Retained candidates exhausted."); } - if (num_expensive_regions_selected > 0) { + + if (num_expensive_regions > 0) { log_debug(gc, ergo, cset)("Added %u retained candidates to collection set although the predicted time was too high.", - num_expensive_regions_selected); + num_expensive_regions); } + // Remove groups from retained and also do some bookkeeping on CandidateOrigin + // for the regions in these groups. + candidates()->remove(&remove_from_retained); + + groups_to_abandon.clear(true /* uninstall_group_cardset */); + log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " "time remaining: %1.2fms optional time remaining %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + num_initial_regions, num_optional_regions, num_pinned_regions, predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); } -void G1CollectionSet::select_candidates_from_optional_regions(double time_remaining_ms, - G1CollectionCandidateRegionList* selected_regions) { - assert(optional_region_length() > 0, +double G1CollectionSet::select_candidates_from_optional_groups(double time_remaining_ms, uint& num_regions_selected) { + + assert(_optional_groups.num_regions() > 0, "Should only be called when there are optional regions"); + uint num_groups_selected = 0; double total_prediction_ms = 0.0; + G1CSetCandidateGroupList selected; + for (G1CSetCandidateGroup* group : _optional_groups) { + double predicted_time_ms = group->predict_group_total_time_ms(); - for (G1HeapRegion* r : _optional_old_regions) { - double prediction_ms = _policy->predict_region_total_time_ms(r, false); - - if (prediction_ms > time_remaining_ms) { - log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.", - prediction_ms, r->hrm_index(), time_remaining_ms); + if (predicted_time_ms > time_remaining_ms) { + log_debug(gc, ergo, cset)("Prediction %.3fms for group with %u regions does not fit remaining time: %.3fms.", + predicted_time_ms, group->length(), time_remaining_ms); break; } - // This region will be included in the next optional evacuation. - total_prediction_ms += prediction_ms; - time_remaining_ms -= prediction_ms; + total_prediction_ms += predicted_time_ms; + time_remaining_ms -= predicted_time_ms; - selected_regions->append(r); + num_regions_selected += group->length(); + num_groups_selected++; + + add_group_to_collection_set(group); + selected.append(group); } - log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", - selected_regions->length(), _optional_old_regions.length(), total_prediction_ms); + log_debug(gc, ergo, cset) ("Completed with groups, selected %u", num_regions_selected); + // Remove selected groups from candidate list. + if (num_groups_selected > 0) { + _optional_groups.remove(&selected); + candidates()->remove(&selected); + } + return total_prediction_ms; } -void G1CollectionSet::prepare_optional_regions(G1CollectionCandidateRegionList* regions){ - uint cur_index = 0; - for (G1HeapRegion* r : *regions) { +uint G1CollectionSet::select_optional_collection_set_regions(double time_remaining_ms) { + uint optional_regions_count = num_optional_regions(); + assert(optional_regions_count > 0, + "Should only be called when there are optional regions"); + + uint num_regions_selected = 0; + + double total_prediction_ms = select_candidates_from_optional_groups(time_remaining_ms, num_regions_selected); + + time_remaining_ms -= total_prediction_ms; + + log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", + num_regions_selected, optional_regions_count, total_prediction_ms); + return num_regions_selected; +} + +void G1CollectionSet::prepare_optional_group(G1CSetCandidateGroup* gr, uint cur_index) { + for (G1CollectionSetCandidateInfo ci : *gr) { + G1HeapRegion* r = ci._r; + assert(r->is_old(), "the region should be old"); assert(!r->in_collection_set(), "should not already be in the CSet"); _g1h->register_optional_region_with_region_attr(r); - r->set_index_in_opt_cset(cur_index++); } } -void G1CollectionSet::move_pinned_marking_to_retained(G1CollectionCandidateRegionList* regions) { - if (regions->length() == 0) { - return; +void G1CollectionSet::add_group_to_collection_set(G1CSetCandidateGroup* gr) { + for (G1CollectionSetCandidateInfo ci : *gr) { + G1HeapRegion* r = ci._r; + r->uninstall_cset_group(); + assert(r->rem_set()->is_complete(), "must be"); + add_region_to_collection_set(r); } - candidates()->remove(regions); - - for (G1HeapRegion* r : *regions) { - assert(r->has_pinned_objects(), "must be pinned"); - assert(r->rem_set()->is_complete(), "must be complete"); - candidates()->add_retained_region_unsorted(r); - } - candidates()->sort_by_efficiency(); + _collection_set_groups.append(gr); } -void G1CollectionSet::drop_pinned_retained_regions(G1CollectionCandidateRegionList* regions) { - if (regions->length() == 0) { - return; - } - candidates()->remove(regions); - - // We can now drop these region's remembered sets. - for (G1HeapRegion* r : *regions) { - r->rem_set()->clear(true /* only_cardset */); - } +void G1CollectionSet::add_region_to_collection_set(G1HeapRegion* r) { + _g1h->clear_region_attr(r); + assert(r->rem_set()->is_complete(), "Remset for region %u complete", r->hrm_index()); + add_old_region(r); } void G1CollectionSet::finalize_initial_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) { @@ -644,31 +671,31 @@ void G1CollectionSet::finalize_initial_collection_set(double target_pause_time_m bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_time) { update_incremental_marker(); - G1CollectionCandidateRegionList selected_regions; - select_candidates_from_optional_regions(remaining_pause_time, - &selected_regions); - - move_candidates_to_collection_set(&selected_regions); - - _optional_old_regions.remove_prefix(&selected_regions); + uint num_regions_selected = select_optional_collection_set_regions(remaining_pause_time); + _selected_groups_cur_length = collection_set_groups()->length(); stop_incremental_building(); _g1h->verify_region_attr_remset_is_tracked(); - return selected_regions.length() > 0; + return num_regions_selected > 0; } void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* pss) { - for (G1HeapRegion* r : _optional_old_regions) { - pss->record_unused_optional_region(r); - // Clear collection set marker and make sure that the remembered set information - // is correct as we still need it later. - _g1h->clear_region_attr(r); - _g1h->register_region_with_region_attr(r); - r->clear_index_in_opt_cset(); + if (_optional_groups.length() > 0) { + auto reset = [&] (G1HeapRegion* r) { + pss->record_unused_optional_region(r); + // Clear collection set marker and make sure that the remembered set information + // is correct as we still need it later. + _g1h->clear_region_attr(r); + _g1h->register_region_with_region_attr(r); + r->clear_index_in_opt_cset(); + }; + + _optional_groups.iterate(reset); + // Remove groups from list without deleting the groups or clearing the associated cardsets. + _optional_groups.remove_selected(_optional_groups.length(), _optional_groups.num_regions()); } - _optional_old_regions.clear(); _g1h->verify_region_attr_remset_is_tracked(); } diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index 5280ba7d0fd..c703e13a056 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -146,13 +146,21 @@ class G1CollectionSet { volatile uint _collection_set_cur_length; uint _collection_set_max_length; + // Old gen groups selected for evacuation. + G1CSetCandidateGroupList _collection_set_groups; + + // Groups are added to the collection set in increments when performing optional evacuations. + // We use the value below to track these increments. + uint _selected_groups_cur_length; + uint _selected_groups_inc_part_start; + uint _eden_region_length; uint _survivor_region_length; uint _initial_old_region_length; // When doing mixed collections we can add old regions to the collection set, which // will be collected only if there is enough time. We call these optional (old) regions. - G1CollectionCandidateRegionList _optional_old_regions; + G1CSetCandidateGroupList _optional_groups; enum CSetBuildType { Active, // We are actively building the collection set @@ -173,16 +181,20 @@ class G1CollectionSet { // Add the given old region to the head of the current collection set. void add_old_region(G1HeapRegion* hr); - void move_candidates_to_collection_set(G1CollectionCandidateRegionList* regions); - // Prepares old regions in the given set for optional collection later. Does not - // add the region to collection set yet. - void prepare_optional_regions(G1CollectionCandidateRegionList* regions); - // Moves given old regions from the marking candidates to the retained candidates. - // This makes sure that marking candidates will not remain there to unnecessarily - // prolong the mixed phase. - void move_pinned_marking_to_retained(G1CollectionCandidateRegionList* regions); - // Removes the given list of regions from the retained candidates. - void drop_pinned_retained_regions(G1CollectionCandidateRegionList* regions); + void prepare_optional_group(G1CSetCandidateGroup* gr, uint cur_index); + + void add_group_to_collection_set(G1CSetCandidateGroup* gr); + + void add_region_to_collection_set(G1HeapRegion* r); + + double select_candidates_from_marking(double time_remaining_ms); + + void select_candidates_from_retained(double time_remaining_ms); + + // Select regions for evacuation from the optional candidates given the remaining time + // and return the number of actually selected regions. + uint select_optional_collection_set_regions(double time_remaining_ms); + double select_candidates_from_optional_groups(double time_remaining_ms, uint& num_regions_selected); // Finalize the young part of the initial collection set. Relabel survivor regions // as Eden and calculate a prediction on how long the evacuation of all young regions @@ -196,22 +208,6 @@ class G1CollectionSet { // and retained collection set candidates. void finalize_old_part(double time_remaining_ms); - // Calculate and fill in the initial, optional and pinned old gen candidate regions from - // the given candidate list and the remaining time. - // Returns the remaining time. - double select_candidates_from_marking(double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - void select_candidates_from_retained(double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - // Calculate the number of optional regions from the given collection set candidates, - // the remaining time and the maximum number of these regions. - void select_candidates_from_optional_regions(double time_remaining_ms, - G1CollectionCandidateRegionList* selected); - // Iterate the part of the collection set given by the offset and length applying the given // G1HeapRegionClosure. The worker_id will determine where in the part to start the iteration // to allow for more efficient parallel iteration. @@ -232,6 +228,11 @@ public: G1CollectionSetCandidates* candidates() { return &_candidates; } const G1CollectionSetCandidates* candidates() const { return &_candidates; } + G1CSetCandidateGroupList* collection_set_groups() { return &_collection_set_groups; } + const G1CSetCandidateGroupList* collection_set_groups() const { return &_collection_set_groups; } + + void prepare_groups_for_scan(); + void init_region_lengths(uint eden_cset_region_length, uint survivor_cset_region_length); @@ -243,9 +244,12 @@ public: uint eden_region_length() const { return _eden_region_length; } uint survivor_region_length() const { return _survivor_region_length; } uint initial_old_region_length() const { return _initial_old_region_length; } - uint optional_region_length() const { return _optional_old_regions.length(); } + uint num_optional_regions() const { return _optional_groups.num_regions(); } - bool only_contains_young_regions() const { return (initial_old_region_length() + optional_region_length()) == 0; } + bool only_contains_young_regions() const { return (initial_old_region_length() + num_optional_regions()) == 0; } + + template + inline void merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers); // Reset the contents of the collection set. void clear(); @@ -255,7 +259,11 @@ public: // Initialize incremental collection set info. void start_incremental_building(); // Start a new collection set increment. - void update_incremental_marker() { _inc_build_state = Active; _inc_part_start = _collection_set_cur_length; } + void update_incremental_marker() { + _inc_build_state = Active; + _inc_part_start = _collection_set_cur_length; + _selected_groups_inc_part_start = _selected_groups_cur_length; + } // Stop adding regions to the current collection set increment. void stop_incremental_building() { _inc_build_state = Inactive; } @@ -268,6 +276,8 @@ public: // Returns the length of the whole current collection set in number of regions size_t cur_length() const { return _collection_set_cur_length; } + uint collection_groups_increment_length() const { return _selected_groups_cur_length - _selected_groups_inc_part_start; } + // Iterate over the entire collection set (all increments calculated so far), applying // the given G1HeapRegionClosure on all of them. void iterate(G1HeapRegionClosure* cl) const; diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp new file mode 100644 index 00000000000..f295eff3edd --- /dev/null +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1COLLECTIONSET_INLINE_HPP +#define SHARE_GC_G1_G1COLLECTIONSET_INLINE_HPP + +#include "gc/g1/g1CollectionSet.hpp" +#include "gc/g1/g1HeapRegionRemSet.hpp" + +template +inline void G1CollectionSet::merge_cardsets_for_collection_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers) { + uint length = collection_groups_increment_length(); + uint offset = _selected_groups_inc_part_start; + if (length == 0) { + return; + } + + uint start_pos = (worker_id * length) / num_workers; + uint cur_pos = start_pos; + uint count = 0; + do { + G1HeapRegionRemSet::iterate_for_merge(collection_set_groups()->at(offset + cur_pos)->card_set(), cl); + cur_pos++; + count++; + if (cur_pos == length) { + cur_pos = 0; + } + } while (cur_pos != start_pos); +} +#endif /* SHARE_GC_G1_G1COLLECTIONSET_INLINE_HPP */ diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 1c3134eee64..5126a46785e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -25,79 +25,101 @@ #include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" -#include "utilities/bitMap.inline.hpp" #include "utilities/growableArray.hpp" -G1CollectionCandidateList::G1CollectionCandidateList() : _candidates(2, mtGC) { } +uint G1CSetCandidateGroup::_next_group_id = 2; -void G1CollectionCandidateList::set(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos) { - assert(_candidates.is_empty(), "must be"); +G1CSetCandidateGroup::G1CSetCandidateGroup(G1CardSetConfiguration* config, G1MonotonicArenaFreePool* card_set_freelist_pool, uint group_id) : + _candidates(4, mtGCCardSet), + _card_set_mm(config, card_set_freelist_pool), + _card_set(config, &_card_set_mm), + _reclaimable_bytes(size_t(0)), + _gc_efficiency(0.0), + _group_id(group_id) +{ } - GrowableArrayFromArray a(candidate_infos, (int)num_infos); - _candidates.appendAll(&a); +G1CSetCandidateGroup::G1CSetCandidateGroup() : + G1CSetCandidateGroup(G1CollectedHeap::heap()->card_set_config(), G1CollectedHeap::heap()->card_set_freelist_pool(), _next_group_id++) +{ } + +void G1CSetCandidateGroup::add(G1HeapRegion* hr) { + G1CollectionSetCandidateInfo c(hr); + add(c); } -void G1CollectionCandidateList::append_unsorted(G1HeapRegion* r) { - G1CollectionSetCandidateInfo c(r, r->calc_gc_efficiency()); - _candidates.append(c); +void G1CSetCandidateGroup::add(G1CollectionSetCandidateInfo& hr_info) { + G1HeapRegion* hr = hr_info._r; + _candidates.append(hr_info); + hr->install_cset_group(this); } -void G1CollectionCandidateList::sort_by_efficiency() { - _candidates.sort(compare_gc_efficiency); -} - -void G1CollectionCandidateList::remove(G1CollectionCandidateRegionList* other) { - guarantee((uint)_candidates.length() >= other->length(), "must be"); - - if (other->length() == 0) { - // Nothing to remove or nothing in the original set. - return; +void G1CSetCandidateGroup::calculate_efficiency() { + _reclaimable_bytes = 0; + uint num_candidates = _candidates.length(); + for (uint i = 0; i < num_candidates; i++) { + G1HeapRegion* hr = region_at(i); + _reclaimable_bytes += hr->reclaimable_bytes(); } + _gc_efficiency = _reclaimable_bytes / predict_group_total_time_ms(); +} - // Create a list from scratch, copying over the elements from the candidate - // list not in the other list. Finally deallocate and overwrite the old list. - int new_length = _candidates.length() - other->length(); - GrowableArray new_list(new_length, mtGC); +size_t G1CSetCandidateGroup::liveness() const { + size_t capacity = length() * G1HeapRegion::GrainBytes; - uint other_idx = 0; + return (size_t) ceil(((capacity - _reclaimable_bytes) * 100.0) / capacity); +} - for (uint candidate_idx = 0; candidate_idx < (uint)_candidates.length(); candidate_idx++) { - if ((other_idx == other->length()) || _candidates.at(candidate_idx)._r != other->at(other_idx)) { - new_list.append(_candidates.at(candidate_idx)); - } else { - other_idx++; +void G1CSetCandidateGroup::clear(bool uninstall_group_cardset) { + if (uninstall_group_cardset) { + for (G1CollectionSetCandidateInfo ci : _candidates) { + G1HeapRegion* r = ci._r; + r->uninstall_cset_group(); + r->rem_set()->clear(true /* only_cardset */); } } - _candidates.swap(&new_list); - - verify(); - assert(_candidates.length() == new_length, "must be %u %u", _candidates.length(), new_length); -} - -void G1CollectionCandidateList::clear() { + _card_set.clear(); _candidates.clear(); } -#ifndef PRODUCT -void G1CollectionCandidateList::verify() { - G1CollectionSetCandidateInfo* prev = nullptr; +double G1CSetCandidateGroup::predict_group_total_time_ms() const { + G1Policy* p = G1CollectedHeap::heap()->policy(); - for (uint i = 0; i < (uint)_candidates.length(); i++) { - G1CollectionSetCandidateInfo& ci = _candidates.at(i); - assert(prev == nullptr || prev->_gc_efficiency >= ci._gc_efficiency, - "Stored gc efficiency must be descending from region %u to %u", - prev->_r->hrm_index(), ci._r->hrm_index()); - prev = &ci; - assert(ci._r->rem_set()->is_tracked(), "remset for region %u must be tracked", ci._r->hrm_index()); + double predicted_copy_time_ms = 0.0; + double predict_code_root_scan_time_ms = 0.0; + size_t predict_bytes_to_copy = 0.0; + + for (G1CollectionSetCandidateInfo ci : _candidates) { + G1HeapRegion* r = ci._r; + assert(r->rem_set()->cset_group() == this, "Must be!"); + + predict_bytes_to_copy += p->predict_bytes_to_copy(r); + predicted_copy_time_ms += p->predict_region_copy_time_ms(r, false /* for_young_only_phase */); + predict_code_root_scan_time_ms += p->predict_region_code_root_scan_time(r, false /* for_young_only_phase */); } + + size_t card_rs_length = _card_set.occupied(); + + double merge_scan_time_ms = p->predict_merge_scan_time(card_rs_length); + double non_young_other_time_ms = p->predict_non_young_other_time_ms(length()); + + log_trace(gc, ergo, cset) ("Prediction for group with %u regions, card_rs_length %zu, merge_scan_time %.2fms, code_root_scan_time_ms %.2fms, evac_time_ms %.2fms, other_time %.2fms, bytes_to_cop %zu", + length(), + card_rs_length, + merge_scan_time_ms, + predict_code_root_scan_time_ms, + predicted_copy_time_ms, + non_young_other_time_ms, + predict_bytes_to_copy); + + return merge_scan_time_ms + + predict_code_root_scan_time_ms + + predicted_copy_time_ms + + non_young_other_time_ms; } -#endif -int G1CollectionCandidateList::compare_gc_efficiency(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) { - assert(ci1->_r != nullptr && ci2->_r != nullptr, "Should not be!"); - - double gc_eff1 = ci1->_gc_efficiency; - double gc_eff2 = ci2->_gc_efficiency; +int G1CSetCandidateGroup::compare_gc_efficiency(G1CSetCandidateGroup** gr1, G1CSetCandidateGroup** gr2) { + double gc_eff1 = (*gr1)->gc_efficiency(); + double gc_eff2 = (*gr2)->gc_efficiency(); if (gc_eff1 > gc_eff2) { return -1; @@ -108,7 +130,7 @@ int G1CollectionCandidateList::compare_gc_efficiency(G1CollectionSetCandidateInf } } -int G1CollectionCandidateList::compare_reclaimble_bytes(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) { +int G1CSetCandidateGroup::compare_reclaimble_bytes(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) { // Make sure that null entries are moved to the end. if (ci1->_r == nullptr) { if (ci2->_r == nullptr) { @@ -132,47 +154,95 @@ int G1CollectionCandidateList::compare_reclaimble_bytes(G1CollectionSetCandidate } } -G1CollectionCandidateRegionList::G1CollectionCandidateRegionList() : _regions(2, mtGC) { } +G1CSetCandidateGroupList::G1CSetCandidateGroupList() : _groups(8, mtGC), _num_regions(0) { } -void G1CollectionCandidateRegionList::append(G1HeapRegion* r) { - assert(!_regions.contains(r), "must be"); - _regions.append(r); +void G1CSetCandidateGroupList::append(G1CSetCandidateGroup* group) { + assert(group->length() > 0, "Do not add empty groups"); + assert(!_groups.contains(group), "Already added to list"); + _groups.append(group); + _num_regions += group->length(); } -void G1CollectionCandidateRegionList::remove_prefix(G1CollectionCandidateRegionList* other) { -#ifdef ASSERT - // Check that the given list is a prefix of this list. - int i = 0; - for (G1HeapRegion* r : *other) { - assert(_regions.at(i) == r, "must be in order, but element %d is not", i); - i++; +G1CSetCandidateGroup* G1CSetCandidateGroupList::at(uint index) { + return _groups.at(index); +} + +void G1CSetCandidateGroupList::clear(bool uninstall_group_cardset) { + for (G1CSetCandidateGroup* gr : _groups) { + gr->clear(uninstall_group_cardset); + delete gr; } -#endif + _groups.clear(); + _num_regions = 0; +} + +void G1CSetCandidateGroupList::prepare_for_scan() { + for (G1CSetCandidateGroup* gr : _groups) { + gr->card_set()->reset_table_scanner_for_groups(); + } +} + +void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions) { + _groups.remove_till(count); + _num_regions -= num_regions; +} + +void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { + guarantee((uint)_groups.length() >= other->length(), "Other should be a subset of this list"); if (other->length() == 0) { + // Nothing to remove or nothing in the original set. return; } - _regions.remove_till(other->length()); + + // Create a list from scratch, copying over the elements from the candidate + // list not in the other list. Finally deallocate and overwrite the old list. + int new_length = _groups.length() - other->length(); + _num_regions = num_regions() - other->num_regions(); + GrowableArray new_list(new_length, mtGC); + + uint other_idx = 0; + for (G1CSetCandidateGroup* gr : _groups) { + if (other_idx == other->length() || gr != other->at(other_idx)) { + new_list.append(gr); + } else { + other_idx++; + } + } + _groups.swap(&new_list); + + verify(); + assert(_groups.length() == new_length, "Must be"); } -G1HeapRegion* G1CollectionCandidateRegionList::at(uint index) { - return _regions.at(index); +void G1CSetCandidateGroupList::sort_by_efficiency() { + _groups.sort(G1CSetCandidateGroup::compare_gc_efficiency); } -void G1CollectionCandidateRegionList::clear() { - _regions.clear(); +#ifndef PRODUCT +void G1CSetCandidateGroupList::verify() const { + G1CSetCandidateGroup* prev = nullptr; + + for (G1CSetCandidateGroup* gr : _groups) { + assert(prev == nullptr || prev->gc_efficiency() >= gr->gc_efficiency(), + "Stored gc efficiency must be descending"); + prev = gr; + } } +#endif G1CollectionSetCandidates::G1CollectionSetCandidates() : - _marking_regions(), - _retained_regions(), _contains_map(nullptr), + _from_marking_groups(), + _retained_groups(), _max_regions(0), _last_marking_candidates_length(0) { } G1CollectionSetCandidates::~G1CollectionSetCandidates() { FREE_C_HEAP_ARRAY(CandidateOrigin, _contains_map); + _from_marking_groups.clear(); + _retained_groups.clear(); } bool G1CollectionSetCandidates::is_from_marking(G1HeapRegion* r) const { @@ -188,8 +258,8 @@ void G1CollectionSetCandidates::initialize(uint max_regions) { } void G1CollectionSetCandidates::clear() { - _marking_regions.clear(); - _retained_regions.clear(); + _retained_groups.clear(true /* uninstall_group_cardset */); + _from_marking_groups.clear(true /* uninstall_group_cardset */); for (uint i = 0; i < _max_regions; i++) { _contains_map[i] = CandidateOrigin::Invalid; } @@ -197,28 +267,62 @@ void G1CollectionSetCandidates::clear() { } void G1CollectionSetCandidates::sort_marking_by_efficiency() { - G1CollectionCandidateListIterator iter = _marking_regions.begin(); - for (; iter != _marking_regions.end(); ++iter) { - G1HeapRegion* hr = (*iter)->_r; - (*iter)->_gc_efficiency = hr->calc_gc_efficiency(); + for (G1CSetCandidateGroup* gr : _from_marking_groups) { + gr->calculate_efficiency(); } - _marking_regions.sort_by_efficiency(); + _from_marking_groups.sort_by_efficiency(); - _marking_regions.verify(); + _from_marking_groups.verify(); } void G1CollectionSetCandidates::set_candidates_from_marking(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos) { - assert(_marking_regions.length() == 0, "must be empty before adding new ones"); + if (num_infos == 0) { + log_debug(gc, ergo, cset) ("No regions selected from marking."); + return; + } + assert(_from_marking_groups.length() == 0, "must be empty at the start of a cycle"); verify(); - _marking_regions.set(candidate_infos, num_infos); + G1Policy* p = G1CollectedHeap::heap()->policy(); + // During each Mixed GC, we must collect at least G1Policy::calc_min_old_cset_length regions to meet + // the G1MixedGCCountTarget. For the first collection in a Mixed GC cycle, we can add all regions + // required to meet this threshold to the same remset group. We are certain these will be collected in + // the same MixedGC. + uint group_limit = p->calc_min_old_cset_length(num_infos); + + uint num_added_to_group = 0; + + G1CSetCandidateGroup::reset_next_group_id(); + G1CSetCandidateGroup* current = nullptr; + + current = new G1CSetCandidateGroup(); + for (uint i = 0; i < num_infos; i++) { G1HeapRegion* r = candidate_infos[i]._r; assert(!contains(r), "must not contain region %u", r->hrm_index()); _contains_map[r->hrm_index()] = CandidateOrigin::Marking; + + if (num_added_to_group == group_limit) { + if (group_limit != G1OldCSetGroupSize) { + group_limit = G1OldCSetGroupSize; + } + + _from_marking_groups.append(current); + + current = new G1CSetCandidateGroup(); + num_added_to_group = 0; + } + current->add(candidate_infos[i]); + num_added_to_group++; } + + _from_marking_groups.append(current); + + assert(_from_marking_groups.num_regions() == num_infos, "Must be!"); + + log_debug(gc, ergo, cset) ("Finished creating %u collection groups from %u regions", _from_marking_groups.length(), num_infos); _last_marking_candidates_length = num_infos; verify(); @@ -227,46 +331,53 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1CollectionSetCandi void G1CollectionSetCandidates::sort_by_efficiency() { // From marking regions must always be sorted so no reason to actually sort // them. - _marking_regions.verify(); - _retained_regions.sort_by_efficiency(); - _retained_regions.verify(); + _from_marking_groups.verify(); + _retained_groups.sort_by_efficiency(); + _retained_groups.verify(); } -void G1CollectionSetCandidates::add_retained_region_unsorted(G1HeapRegion* r) { - assert(!contains(r), "must not contain region %u", r->hrm_index()); - _contains_map[r->hrm_index()] = CandidateOrigin::Retained; - _retained_regions.append_unsorted(r); -} - -void G1CollectionSetCandidates::remove(G1CollectionCandidateRegionList* other) { +void G1CollectionSetCandidates::remove(G1CSetCandidateGroupList* other) { // During removal, we exploit the fact that elements in the marking_regions, // retained_regions and other list are sorted by gc_efficiency. Furthermore, // all regions in the passed other list are in one of the two other lists. // // Split original list into elements for the marking list and elements from the // retained list. - G1CollectionCandidateRegionList other_marking_regions; - G1CollectionCandidateRegionList other_retained_regions; + G1CSetCandidateGroupList other_marking_groups; + G1CSetCandidateGroupList other_retained_groups; - for (G1HeapRegion* r : *other) { + for (G1CSetCandidateGroup* group : *other) { + assert(group->length() > 0, "Should not have empty groups"); + // Regions in the same group have the same source (i.e from_marking or retained). + G1HeapRegion* r = group->region_at(0); if (is_from_marking(r)) { - other_marking_regions.append(r); + other_marking_groups.append(group); } else { - other_retained_regions.append(r); + other_retained_groups.append(group); } } - _marking_regions.remove(&other_marking_regions); - _retained_regions.remove(&other_retained_regions); + _from_marking_groups.remove(&other_marking_groups); + _retained_groups.remove(&other_retained_groups); - for (G1HeapRegion* r : *other) { - assert(contains(r), "must contain region %u", r->hrm_index()); + other->iterate([&] (G1HeapRegion* r) { + assert(contains(r), "Must contain region %u", r->hrm_index()); _contains_map[r->hrm_index()] = CandidateOrigin::Invalid; - } + }); verify(); } +void G1CollectionSetCandidates::add_retained_region_unsorted(G1HeapRegion* r) { + assert(!contains(r), "Must not already contain region %u", r->hrm_index()); + _contains_map[r->hrm_index()] = CandidateOrigin::Retained; + + G1CSetCandidateGroup* gr = new G1CSetCandidateGroup(); + gr->add(r); + + _retained_groups.append(gr); +} + bool G1CollectionSetCandidates::is_empty() const { return length() == 0; } @@ -276,29 +387,31 @@ bool G1CollectionSetCandidates::has_more_marking_candidates() const { } uint G1CollectionSetCandidates::marking_regions_length() const { - return _marking_regions.length(); + return _from_marking_groups.num_regions(); } uint G1CollectionSetCandidates::retained_regions_length() const { - return _retained_regions.length(); + return _retained_groups.num_regions(); } #ifndef PRODUCT -void G1CollectionSetCandidates::verify_helper(G1CollectionCandidateList* list, uint& from_marking, CandidateOrigin* verify_map) { +void G1CollectionSetCandidates::verify_helper(G1CSetCandidateGroupList* list, uint& from_marking, CandidateOrigin* verify_map) { list->verify(); - for (uint i = 0; i < (uint)list->length(); i++) { - G1HeapRegion* r = list->at(i)._r; + for (G1CSetCandidateGroup* gr : *list) { + for (G1CollectionSetCandidateInfo ci : *gr) { + G1HeapRegion* r = ci._r; - if (is_from_marking(r)) { - from_marking++; + if (is_from_marking(r)) { + from_marking++; + } + const uint hrm_index = r->hrm_index(); + assert(_contains_map[hrm_index] == CandidateOrigin::Marking || _contains_map[hrm_index] == CandidateOrigin::Retained, + "must be %u is %u", hrm_index, (uint)_contains_map[hrm_index]); + assert(verify_map[hrm_index] == CandidateOrigin::Invalid, "already added"); + + verify_map[hrm_index] = CandidateOrigin::Verify; } - const uint hrm_index = r->hrm_index(); - assert(_contains_map[hrm_index] == CandidateOrigin::Marking || _contains_map[hrm_index] == CandidateOrigin::Retained, - "must be %u is %u", hrm_index, (uint)_contains_map[hrm_index]); - assert(verify_map[hrm_index] == CandidateOrigin::Invalid, "already added"); - - verify_map[hrm_index] = CandidateOrigin::Verify; } } @@ -310,11 +423,11 @@ void G1CollectionSetCandidates::verify() { verify_map[i] = CandidateOrigin::Invalid; } - verify_helper(&_marking_regions, from_marking, verify_map); + verify_helper(&_from_marking_groups, from_marking, verify_map); assert(from_marking == marking_regions_length(), "must be"); uint from_marking_retained = 0; - verify_helper(&_retained_regions, from_marking_retained, verify_map); + verify_helper(&_retained_groups, from_marking_retained, verify_map); assert(from_marking_retained == 0, "must be"); assert(length() >= marking_regions_length(), "must be"); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index e629aa8f363..b60af7d067d 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -25,53 +25,24 @@ #ifndef SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP #define SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP +#include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/shared/gc_globals.hpp" -#include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" #include "runtime/globals.hpp" -#include "utilities/bitMap.hpp" #include "utilities/growableArray.hpp" -class G1CollectionCandidateList; class G1CollectionSetCandidates; +class G1CSetCandidateGroupList; class G1HeapRegion; class G1HeapRegionClosure; -using G1CollectionCandidateRegionListIterator = GrowableArrayIterator; - -// A set of G1HeapRegion*, a thin wrapper around GrowableArray. -class G1CollectionCandidateRegionList { - GrowableArray _regions; - -public: - G1CollectionCandidateRegionList(); - - // Append a G1HeapRegion to the end of this list. The region must not be in the list - // already. - void append(G1HeapRegion* r); - // Remove the given list of G1HeapRegion* from this list. The given list must be a prefix - // of this list. - void remove_prefix(G1CollectionCandidateRegionList* list); - - // Empty contents of the list. - void clear(); - - G1HeapRegion* at(uint index); - - uint length() const { return (uint)_regions.length(); } - - G1CollectionCandidateRegionListIterator begin() const { return _regions.begin(); } - G1CollectionCandidateRegionListIterator end() const { return _regions.end(); } -}; - struct G1CollectionSetCandidateInfo { G1HeapRegion* _r; - double _gc_efficiency; uint _num_unreclaimed; // Number of GCs this region has been found unreclaimable. - G1CollectionSetCandidateInfo() : G1CollectionSetCandidateInfo(nullptr, 0.0) { } - G1CollectionSetCandidateInfo(G1HeapRegion* r, double gc_efficiency) : _r(r), _gc_efficiency(gc_efficiency), _num_unreclaimed(0) { } + G1CollectionSetCandidateInfo() : G1CollectionSetCandidateInfo(nullptr) { } + G1CollectionSetCandidateInfo(G1HeapRegion* r) : _r(r), _num_unreclaimed(0) { } bool update_num_unreclaimed() { ++_num_unreclaimed; @@ -79,100 +50,159 @@ struct G1CollectionSetCandidateInfo { } }; -class G1CollectionCandidateListIterator : public StackObj { - G1CollectionCandidateList* _which; - uint _position; - -public: - G1CollectionCandidateListIterator(G1CollectionCandidateList* which, uint position); - - G1CollectionCandidateListIterator& operator++(); - G1CollectionSetCandidateInfo* operator*(); - - bool operator==(const G1CollectionCandidateListIterator& rhs); - bool operator!=(const G1CollectionCandidateListIterator& rhs); -}; - -// List of collection set candidates (regions with their efficiency) ordered by -// decreasing gc efficiency. -class G1CollectionCandidateList : public CHeapObj { - friend class G1CollectionCandidateListIterator; +using G1CSetCandidateGroupIterator = GrowableArrayIterator; +// G1CSetCandidateGroup groups candidate regions that will be selected for evacuation at the same time. +// Grouping occurs both for candidates from marking or regions retained during evacuation failure, but a group +// can not contain regions from both types of regions. +// +// Humongous objects are excluded from the candidate groups because regions associated with these +// objects are never selected for evacuation. +// +// All regions in the group share a G1CardSet instance, which tracks remembered set entries for the +// regions in the group. We do not have track to cross-region references for regions that are in the +// same group saving memory. +class G1CSetCandidateGroup : public CHeapObj{ GrowableArray _candidates; + G1CardSetMemoryManager _card_set_mm; + + // The set of cards in the Java heap + G1CardSet _card_set; + + size_t _reclaimable_bytes; + double _gc_efficiency; + + // The _group_id is primarily used when printing out per-region liveness information, + // making it easier to associate regions with their assigned G1CSetCandidateGroup, if any. + // Note: + // * _group_id 0 is reserved for special G1CSetCandidateGroups that hold only a single region, + // such as G1CSetCandidateGroups for retained regions. + // * _group_id 1 is reserved for the G1CSetCandidateGroup that contains all young regions. + const uint _group_id; + static uint _next_group_id; public: - G1CollectionCandidateList(); + G1CSetCandidateGroup(); + G1CSetCandidateGroup(G1CardSetConfiguration* config, G1MonotonicArenaFreePool* card_set_freelist_pool, uint group_id); + ~G1CSetCandidateGroup() { + assert(length() == 0, "post condition!"); + } - // Put the given set of candidates into this list, preserving the efficiency ordering. - void set(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos); - // Add the given G1HeapRegion to this list at the end, (potentially) making the list unsorted. - void append_unsorted(G1HeapRegion* r); - // Restore sorting order by decreasing gc efficiency, using the existing efficiency - // values. - void sort_by_efficiency(); - // Removes any heap regions stored in this list also in the other list. The other - // list may only contain regions in this list, sorted by gc efficiency. It need - // not be a prefix of this list. Returns the number of regions removed. - // E.g. if this list is "A B G H", the other list may be "A G H", but not "F" (not in - // this list) or "A H G" (wrong order). - void remove(G1CollectionCandidateRegionList* other); - - void clear(); - - G1CollectionSetCandidateInfo& at(uint position) { return _candidates.at(position); } + void add(G1HeapRegion* hr); + void add(G1CollectionSetCandidateInfo& hr_info); uint length() const { return (uint)_candidates.length(); } - void verify() PRODUCT_RETURN; + G1CardSet* card_set() { return &_card_set; } + const G1CardSet* card_set() const { return &_card_set; } + uint group_id() const { return _group_id; } + + void calculate_efficiency(); + + size_t liveness() const; // Comparison function to order regions in decreasing GC efficiency order. This // will cause regions with a lot of live objects and large remembered sets to end // up at the end of the list. - static int compare_gc_efficiency(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2); + static int compare_gc_efficiency(G1CSetCandidateGroup** gr1, G1CSetCandidateGroup** gr2); static int compare_reclaimble_bytes(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2); - G1CollectionCandidateListIterator begin() { - return G1CollectionCandidateListIterator(this, 0); + double gc_efficiency() const { return _gc_efficiency; } + + G1HeapRegion* region_at(uint i) const { return _candidates.at(i)._r; } + + G1CollectionSetCandidateInfo* at(uint i) { return &_candidates.at(i); } + + double predict_group_total_time_ms() const; + + G1MonotonicArenaMemoryStats card_set_memory_stats() const { + return _card_set_mm.memory_stats(); } - G1CollectionCandidateListIterator end() { - return G1CollectionCandidateListIterator(this, length()); + void clear(bool uninstall_group_cardset = false); + + G1CSetCandidateGroupIterator begin() const { + return _candidates.begin(); + } + + G1CSetCandidateGroupIterator end() const { + return _candidates.end(); + } + + static void reset_next_group_id() { + _next_group_id = 2; } }; -// Iterator for G1CollectionSetCandidates. There are no guarantees on the order -// of the regions returned. -class G1CollectionSetCandidatesIterator : public StackObj { - G1CollectionSetCandidates* _which; - uint _position; +using G1CSetCandidateGroupListIterator = GrowableArrayIterator; - public: - G1CollectionSetCandidatesIterator(G1CollectionSetCandidates* which, uint position); +class G1CSetCandidateGroupList { + GrowableArray _groups; + volatile uint _num_regions; - G1CollectionSetCandidatesIterator& operator++(); - G1HeapRegion* operator*(); +public: + G1CSetCandidateGroupList(); + void append(G1CSetCandidateGroup* group); - bool operator==(const G1CollectionSetCandidatesIterator& rhs); - bool operator!=(const G1CollectionSetCandidatesIterator& rhs); + // Delete all groups from the list. The cardset cleanup for regions within the + // groups could have been done elsewhere (e.g. when adding groups to the + // collection set or to retained regions). The uninstall_group_cardset is set to + // true if cleanup needs to happen as we clear the groups from the list. + void clear(bool uninstall_group_cardset = false); + + G1CSetCandidateGroup* at(uint index); + + uint length() const { return (uint)_groups.length(); } + + uint num_regions() const { return _num_regions; } + + void remove_selected(uint count, uint num_regions); + + // Removes any candidate groups stored in this list and also in the other list. The other + // list may only contain candidate groups in this list, sorted by gc efficiency. It need + // not be a prefix of this list. + // E.g. if this list is "A B G H", the other list may be "A G H", but not "F" (not in + // this list) or "A H G" (wrong order). + void remove(G1CSetCandidateGroupList* other); + + void prepare_for_scan(); + + void sort_by_efficiency(); + + GrowableArray* groups() { + return &_groups; + } + + void verify() const PRODUCT_RETURN; + + G1CSetCandidateGroupListIterator begin() const { + return _groups.begin(); + } + + G1CSetCandidateGroupListIterator end() const { + return _groups.end(); + } + + template + void iterate(Func&& f) const; }; -// Tracks all collection set candidates, i.e. regions that could/should be evacuated soon. +// Tracks all collection set candidates, i.e. region groups that could/should be evacuated soon. // -// These candidate regions are tracked in two list of regions, sorted by decreasing +// These candidate groups are tracked in two list of region groups, sorted by decreasing // "gc efficiency". // -// * marking_regions: the set of regions selected by concurrent marking to be -// evacuated to keep overall heap occupancy stable. -// They are guaranteed to be evacuated and cleared out during -// the mixed phase. +// * from_marking_groups: the set of region groups selected by concurrent marking to be +// evacuated to keep overall heap occupancy stable. +// They are guaranteed to be evacuated and cleared out during +// the mixed phase. // -// * retained_regions: set of regions selected for evacuation during evacuation -// failure. -// Any young collection will try to evacuate them. +// * retained_groups: set of region groups selected for evacuation during evacuation +// failure. +// Any young collection will try to evacuate them. // class G1CollectionSetCandidates : public CHeapObj { - friend class G1CollectionSetCandidatesIterator; enum class CandidateOrigin : uint8_t { Invalid, @@ -181,10 +211,12 @@ class G1CollectionSetCandidates : public CHeapObj { Verify // Special value for verification. }; - G1CollectionCandidateList _marking_regions; // Set of regions selected by concurrent marking. - G1CollectionCandidateList _retained_regions; // Set of regions selected from evacuation failed regions. - CandidateOrigin* _contains_map; + G1CSetCandidateGroupList _from_marking_groups; // Set of regions selected by concurrent marking. + // Set of regions retained due to evacuation failure. Groups added to this list + // should contain only one region each, making it easier to evacuate retained regions + // in any young collection. + G1CSetCandidateGroupList _retained_groups; uint _max_regions; // The number of regions from the last merge of candidates from the marking. @@ -196,8 +228,8 @@ public: G1CollectionSetCandidates(); ~G1CollectionSetCandidates(); - G1CollectionCandidateList& marking_regions() { return _marking_regions; } - G1CollectionCandidateList& retained_regions() { return _retained_regions; } + G1CSetCandidateGroupList& from_marking_groups() { return _from_marking_groups; } + G1CSetCandidateGroupList& retained_groups() { return _retained_groups; } void initialize(uint max_regions); @@ -219,9 +251,9 @@ public: // Add the given region to the set of retained regions without regards to the // gc efficiency sorting. The retained regions must be re-sorted manually later. void add_retained_region_unsorted(G1HeapRegion* r); - // Remove the given regions from the candidates. All given regions must be part + // Remove the given groups from the candidates. All given regions must be part // of the candidates. - void remove(G1CollectionCandidateRegionList* other); + void remove(G1CSetCandidateGroupList* other); bool contains(const G1HeapRegion* r) const; @@ -234,21 +266,15 @@ public: uint retained_regions_length() const; private: - void verify_helper(G1CollectionCandidateList* list, uint& from_marking, CandidateOrigin* verify_map) PRODUCT_RETURN; + void verify_helper(G1CSetCandidateGroupList* list, uint& from_marking, CandidateOrigin* verify_map) PRODUCT_RETURN; public: void verify() PRODUCT_RETURN; uint length() const { return marking_regions_length() + retained_regions_length(); } - // Iteration - G1CollectionSetCandidatesIterator begin() { - return G1CollectionSetCandidatesIterator(this, 0); - } - - G1CollectionSetCandidatesIterator end() { - return G1CollectionSetCandidatesIterator(this, length()); - } + template + void iterate_regions(Func&& f) const; }; #endif /* SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP */ diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp index cc2c24fbba0..d32c7990d4e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp @@ -29,54 +29,21 @@ #include "utilities/growableArray.hpp" -inline G1CollectionCandidateListIterator::G1CollectionCandidateListIterator(G1CollectionCandidateList* which, uint position) : - _which(which), _position(position) { } - -inline G1CollectionCandidateListIterator& G1CollectionCandidateListIterator::operator++() { - assert(_position < _which->length(), "must be"); - _position++; - return *this; -} - -inline G1CollectionSetCandidateInfo* G1CollectionCandidateListIterator::operator*() { - return &_which->_candidates.at(_position); -} - -inline bool G1CollectionCandidateListIterator::operator==(const G1CollectionCandidateListIterator& rhs) { - assert(_which == rhs._which, "iterator belongs to different array"); - return _position == rhs._position; -} - -inline bool G1CollectionCandidateListIterator::operator!=(const G1CollectionCandidateListIterator& rhs) { - return !(*this == rhs); -} - -inline G1CollectionSetCandidatesIterator::G1CollectionSetCandidatesIterator(G1CollectionSetCandidates* which, uint position) : - _which(which), _position(position) { -} - -inline G1CollectionSetCandidatesIterator& G1CollectionSetCandidatesIterator::operator++() { - assert(_position < _which->length(), "must not be at end already"); - _position++; - return *this; -} - -inline G1HeapRegion* G1CollectionSetCandidatesIterator::operator*() { - uint length = _which->marking_regions_length(); - if (_position < length) { - return _which->_marking_regions.at(_position)._r; - } else { - return _which->_retained_regions.at(_position - length)._r; +template +void G1CSetCandidateGroupList::iterate(Func&& f) const { + for (G1CSetCandidateGroup* group : _groups) { + for (G1CollectionSetCandidateInfo ci : *group) { + G1HeapRegion* r = ci._r; + f(r); + } } } -inline bool G1CollectionSetCandidatesIterator::operator==(const G1CollectionSetCandidatesIterator& rhs) { - assert(_which == rhs._which, "iterator belongs to different array"); - return _position == rhs._position; -} +template +void G1CollectionSetCandidates::iterate_regions(Func&& f) const { + _from_marking_groups.iterate(f); -inline bool G1CollectionSetCandidatesIterator::operator!=(const G1CollectionSetCandidatesIterator& rhs) { - return !(*this == rhs); + _retained_groups.iterate(f); } #endif /* SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_INLINE_HPP */ diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index 28ca72a2381..3531d97fd3a 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp @@ -93,7 +93,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { void set(uint idx, G1HeapRegion* hr) { assert(idx < _max_size, "Index %u out of bounds %u", idx, _max_size); assert(_data[idx]._r == nullptr, "Value must not have been set."); - _data[idx] = CandidateInfo(hr, 0.0); + _data[idx] = CandidateInfo(hr); } void sort_by_reclaimable_bytes() { @@ -103,7 +103,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { for (uint i = _cur_claim_idx; i < _max_size; i++) { assert(_data[i]._r == nullptr, "must be"); } - qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)G1CollectionCandidateList::compare_reclaimble_bytes); + qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)G1CSetCandidateGroup::compare_reclaimble_bytes); for (uint i = _cur_claim_idx; i < _max_size; i++) { assert(_data[i]._r == nullptr, "must be"); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 3558138ad5b..87b384b1c94 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -2980,6 +2980,11 @@ G1CMTask::G1CMTask(uint worker_id, #define G1PPRL_DOUBLE_FORMAT "%14.1f" #define G1PPRL_GCEFF_FORMAT " %14s" #define G1PPRL_GCEFF_H_FORMAT " %14s" +#define G1PPRL_GID_H_FORMAT " %9s" +#define G1PPRL_GID_FORMAT " " UINT32_FORMAT_W(9) +#define G1PPRL_LEN_FORMAT " " UINT32_FORMAT_W(14) +#define G1PPRL_LEN_H_FORMAT " %14s" +#define G1PPRL_GID_GCEFF_FORMAT " %14.1f" // For summary info #define G1PPRL_SUM_ADDR_FORMAT(tag) " " tag ":" G1PPRL_ADDR_BASE_FORMAT @@ -2992,7 +2997,6 @@ G1PrintRegionLivenessInfoClosure::G1PrintRegionLivenessInfoClosure(const char* p _total_capacity_bytes(0), _total_live_bytes(0), _total_remset_bytes(0), - _young_cardset_bytes_per_region(0), _total_code_roots_bytes(0) { if (!log_is_enabled(Trace, gc, liveness)) { @@ -3003,13 +3007,6 @@ G1PrintRegionLivenessInfoClosure::G1PrintRegionLivenessInfoClosure(const char* p MemRegion reserved = g1h->reserved(); double now = os::elapsedTime(); - uint num_young_regions = g1h->young_regions_count(); - size_t young_cardset_bytes = g1h->young_regions_cardset()->mem_size(); - - if (num_young_regions > 0) { - _young_cardset_bytes_per_region = young_cardset_bytes / num_young_regions; - } - // Print the header of the output. log_trace(gc, liveness)(G1PPRL_LINE_PREFIX" PHASE %s @ %1.3f", phase_name, now); log_trace(gc, liveness)(G1PPRL_LINE_PREFIX" HEAP" @@ -3023,25 +3020,24 @@ G1PrintRegionLivenessInfoClosure::G1PrintRegionLivenessInfoClosure(const char* p G1PPRL_ADDR_BASE_H_FORMAT G1PPRL_BYTE_H_FORMAT G1PPRL_BYTE_H_FORMAT - G1PPRL_GCEFF_H_FORMAT - G1PPRL_BYTE_H_FORMAT G1PPRL_STATE_H_FORMAT - G1PPRL_BYTE_H_FORMAT, + G1PPRL_BYTE_H_FORMAT + G1PPRL_GID_H_FORMAT, "type", "address-range", - "used", "live", "gc-eff", - "remset", "state", "code-roots"); + "used", "live", + "state", "code-roots", + "group-id"); log_trace(gc, liveness)(G1PPRL_LINE_PREFIX G1PPRL_TYPE_H_FORMAT G1PPRL_ADDR_BASE_H_FORMAT G1PPRL_BYTE_H_FORMAT G1PPRL_BYTE_H_FORMAT - G1PPRL_GCEFF_H_FORMAT - G1PPRL_BYTE_H_FORMAT G1PPRL_STATE_H_FORMAT - G1PPRL_BYTE_H_FORMAT, + G1PPRL_BYTE_H_FORMAT + G1PPRL_GID_H_FORMAT, "", "", - "(bytes)", "(bytes)", "(bytes/ms)", - "(bytes)", "", "(bytes)"); + "(bytes)", "(bytes)", + "", "(bytes)", ""); } bool G1PrintRegionLivenessInfoClosure::do_heap_region(G1HeapRegion* r) { @@ -3055,14 +3051,13 @@ bool G1PrintRegionLivenessInfoClosure::do_heap_region(G1HeapRegion* r) { size_t capacity_bytes = r->capacity(); size_t used_bytes = r->used(); size_t live_bytes = r->live_bytes(); - double gc_eff = r->calc_gc_efficiency(); size_t remset_bytes = r->rem_set()->mem_size(); size_t code_roots_bytes = r->rem_set()->code_roots_mem_size(); const char* remset_type = r->rem_set()->get_short_state_str(); - FormatBuffer<16> gc_efficiency(""); + uint cset_groud_gid = 0; - if (r->is_young()) { - remset_bytes = _young_cardset_bytes_per_region; + if (r->rem_set()->is_added_to_cset_group()) { + cset_groud_gid = r->rem_set()->cset_group_id(); } _total_used_bytes += used_bytes; @@ -3071,25 +3066,19 @@ bool G1PrintRegionLivenessInfoClosure::do_heap_region(G1HeapRegion* r) { _total_remset_bytes += remset_bytes; _total_code_roots_bytes += code_roots_bytes; - if(gc_eff < 0) { - gc_efficiency.append("-"); - } else { - gc_efficiency.append(G1PPRL_DOUBLE_FORMAT, gc_eff); - } - // Print a line for this particular region. log_trace(gc, liveness)(G1PPRL_LINE_PREFIX G1PPRL_TYPE_FORMAT G1PPRL_ADDR_BASE_FORMAT G1PPRL_BYTE_FORMAT G1PPRL_BYTE_FORMAT - G1PPRL_GCEFF_FORMAT - G1PPRL_BYTE_FORMAT G1PPRL_STATE_FORMAT - G1PPRL_BYTE_FORMAT, + G1PPRL_BYTE_FORMAT + G1PPRL_GID_FORMAT, type, p2i(bottom), p2i(end), - used_bytes, live_bytes, gc_efficiency.buffer(), - remset_bytes, remset_type, code_roots_bytes); + used_bytes, live_bytes, + remset_type, code_roots_bytes, + cset_groud_gid); return false; } @@ -3103,6 +3092,9 @@ G1PrintRegionLivenessInfoClosure::~G1PrintRegionLivenessInfoClosure() { _total_remset_bytes += g1h->card_set_freelist_pool()->mem_size(); // add static memory usages to remembered set sizes _total_remset_bytes += G1HeapRegionRemSet::static_mem_size(); + + do_cset_groups(); + // Print the footer of the output. log_trace(gc, liveness)(G1PPRL_LINE_PREFIX); log_trace(gc, liveness)(G1PPRL_LINE_PREFIX @@ -3120,3 +3112,77 @@ G1PrintRegionLivenessInfoClosure::~G1PrintRegionLivenessInfoClosure() { bytes_to_mb(_total_remset_bytes), bytes_to_mb(_total_code_roots_bytes)); } + +void G1PrintRegionLivenessInfoClosure::do_cset_groups() { + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX" Collectionset Candidate Groups"); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX " Types: Y=Young Regions, M=From Marking Regions, R=Retained Regions"); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_H_FORMAT + G1PPRL_LEN_H_FORMAT + G1PPRL_GCEFF_H_FORMAT + G1PPRL_BYTE_H_FORMAT + G1PPRL_BYTE_H_FORMAT + G1PPRL_TYPE_H_FORMAT, + "groud-id", "num-regions", + "gc-eff", "liveness", + "remset", "type"); + + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_H_FORMAT + G1PPRL_LEN_H_FORMAT + G1PPRL_GCEFF_H_FORMAT + G1PPRL_BYTE_H_FORMAT + G1PPRL_BYTE_H_FORMAT + G1PPRL_TYPE_H_FORMAT, + "", "", + "(bytes/ms)", "%", + "(bytes)", ""); + + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + G1CSetCandidateGroup* young_only_cset_group =g1h->young_regions_cset_group(); + + _total_remset_bytes += young_only_cset_group->card_set()->mem_size(); + + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_FORMAT + G1PPRL_LEN_FORMAT + G1PPRL_GCEFF_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_TYPE_H_FORMAT, + young_only_cset_group->group_id(), young_only_cset_group->length(), + "-", + size_t(0), young_only_cset_group->card_set()->mem_size(), + "Y"); + + for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->from_marking_groups()) { + _total_remset_bytes += group->card_set()->mem_size(); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_FORMAT + G1PPRL_LEN_FORMAT + G1PPRL_GID_GCEFF_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_TYPE_H_FORMAT, + group->group_id(), group->length(), + group->gc_efficiency(), + group->liveness(), group->card_set()->mem_size(), + "M"); + } + + for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->retained_groups()) { + _total_remset_bytes += group->card_set()->mem_size(); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_FORMAT + G1PPRL_LEN_FORMAT + G1PPRL_GID_GCEFF_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_TYPE_H_FORMAT, + group->group_id(), group->length(), + group->gc_efficiency(), + group->liveness(), group->card_set()->mem_size(), + "R"); + } +} diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index fed624df851..3ffa9fd9c7c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -973,8 +973,6 @@ class G1PrintRegionLivenessInfoClosure : public G1HeapRegionClosure { // Accumulator for the remembered set size size_t _total_remset_bytes; - size_t _young_cardset_bytes_per_region; - // Accumulator for code roots memory size size_t _total_code_roots_bytes; @@ -982,6 +980,8 @@ class G1PrintRegionLivenessInfoClosure : public G1HeapRegionClosure { return (double) val / (double) M; } + void do_cset_groups(); + public: // The header and footer are printed in the constructor and // destructor respectively. diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 6a4e5171786..e9d6d23149f 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -246,7 +246,7 @@ void G1FullCollector::complete_collection() { _heap->resize_all_tlabs(); - _heap->young_regions_cardset()->clear(); + _heap->young_regions_cset_group()->clear(); _heap->policy()->record_full_collection_end(); _heap->gc_epilogue(true); diff --git a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp index 610cba98521..ae9a78a9cdf 100644 --- a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp @@ -36,7 +36,10 @@ void G1FullGCResetMetadataTask::G1ResetMetadataClosure::reset_region_metadata(G1 } bool G1FullGCResetMetadataTask::G1ResetMetadataClosure::do_heap_region(G1HeapRegion* hr) { - hr->uninstall_group_cardset(); + if (!hr->is_humongous()) { + hr->uninstall_cset_group(); + } + uint const region_idx = hr->hrm_index(); if (!_collector->is_compaction_target(region_idx)) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 0377b82a65c..428b708fa56 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -118,7 +118,7 @@ void G1HeapRegion::hr_clear(bool clear_space) { clear_young_index_in_cset(); clear_index_in_opt_cset(); uninstall_surv_rate_group(); - uninstall_group_cardset(); + uninstall_cset_group(); set_free(); reset_pre_dummy_top(); @@ -137,18 +137,6 @@ void G1HeapRegion::clear_cardtable() { ct->clear_MemRegion(MemRegion(bottom(), end())); } -double G1HeapRegion::calc_gc_efficiency() { - // GC efficiency is the ratio of how much space would be - // reclaimed over how long we predict it would take to reclaim it. - G1Policy* policy = G1CollectedHeap::heap()->policy(); - - // Retrieve a prediction of the elapsed time for this region for - // a mixed gc because the region will only be evacuated during a - // mixed gc. - double region_elapsed_time_ms = policy->predict_region_total_time_ms(this, false /* for_young_only_phase */); - return (double)reclaimable_bytes() / region_elapsed_time_ms; -} - void G1HeapRegion::set_free() { if (!is_free()) { report_region_type_change(G1HeapRegionTraceType::Free); @@ -191,6 +179,9 @@ void G1HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) { _type.set_starts_humongous(); _humongous_start_region = this; + G1CSetCandidateGroup* cset_group = new G1CSetCandidateGroup(); + cset_group->add(this); + _bot->update_for_block(bottom(), obj_top); if (fill_size > 0) { _bot->update_for_block(obj_top, obj_top + fill_size); @@ -211,12 +202,19 @@ void G1HeapRegion::clear_humongous() { assert(is_humongous(), "pre-condition"); assert(capacity() == G1HeapRegion::GrainBytes, "pre-condition"); + if (is_starts_humongous()) { + G1CSetCandidateGroup* cset_group = _rem_set->cset_group(); + assert(cset_group != nullptr, "pre-condition %u missing cardset", hrm_index()); + uninstall_cset_group(); + cset_group->clear(); + delete cset_group; + } _humongous_start_region = nullptr; } void G1HeapRegion::prepare_remset_for_scan() { if (is_young()) { - uninstall_group_cardset(); + uninstall_cset_group(); } _rem_set->reset_table_scanner(); } @@ -250,7 +248,7 @@ G1HeapRegion::G1HeapRegion(uint hrm_index, assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()), "invalid space boundaries"); - _rem_set = new G1HeapRegionRemSet(this, config); + _rem_set = new G1HeapRegionRemSet(this); initialize(); } @@ -600,7 +598,9 @@ class G1VerifyLiveAndRemSetClosure : public BasicOopIterateClosure { } bool failed() const { - if (_from != _to && !_from->is_young() && _to->rem_set()->is_complete()) { + if (_from != _to && !_from->is_young() && + _to->rem_set()->is_complete() && + _from->rem_set()->cset_group() != _to->rem_set()->cset_group()) { const CardValue dirty = G1CardTable::dirty_card_val(); return !(_to->rem_set()->contains_reference(this->_p) || (this->_containing_obj->is_objArray() ? diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index c17183d4034..fbf13e1b571 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -40,6 +40,7 @@ class G1CardSet; class G1CardSetConfiguration; class G1CollectedHeap; class G1CMBitMap; +class G1CSetCandidateGroup; class G1Predictions; class G1HeapRegion; class G1HeapRegionRemSet; @@ -488,8 +489,6 @@ public: void set_index_in_opt_cset(uint index) { _index_in_opt_cset = index; } void clear_index_in_opt_cset() { _index_in_opt_cset = InvalidCSetIndex; } - double calc_gc_efficiency(); - uint young_index_in_cset() const { return _young_index_in_cset; } void clear_young_index_in_cset() { _young_index_in_cset = 0; } void set_young_index_in_cset(uint index) { @@ -509,8 +508,8 @@ public: void install_surv_rate_group(G1SurvRateGroup* surv_rate_group); void uninstall_surv_rate_group(); - void install_group_cardset(G1CardSet* group_cardset); - void uninstall_group_cardset(); + void install_cset_group(G1CSetCandidateGroup* cset_group); + void uninstall_cset_group(); void record_surv_words_in_group(size_t words_survived); diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index 4a87c5f2514..7c4342d3371 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -511,12 +511,12 @@ inline void G1HeapRegion::add_pinned_object_count(size_t value) { Atomic::add(&_pinned_object_count, value, memory_order_relaxed); } -inline void G1HeapRegion::install_group_cardset(G1CardSet* group_cardset) { - _rem_set->install_group_cardset(group_cardset); +inline void G1HeapRegion::install_cset_group(G1CSetCandidateGroup* cset_group) { + _rem_set->install_cset_group(cset_group); } -inline void G1HeapRegion::uninstall_group_cardset() { - _rem_set->uninstall_group_cardset(); +inline void G1HeapRegion::uninstall_cset_group() { + _rem_set->uninstall_cset_group(); } #endif // SHARE_GC_G1_G1HEAPREGION_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index 7196e8b2db2..7852bb7e8e3 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -52,48 +52,52 @@ void G1HeapRegionRemSet::initialize(MemRegion reserved) { _heap_base_address = reserved.start(); } -void G1HeapRegionRemSet::uninstall_group_cardset() { - if (_saved_card_set != nullptr) { - _card_set = _saved_card_set; - _saved_card_set = nullptr; - } +void G1HeapRegionRemSet::uninstall_cset_group() { + _cset_group = nullptr; } -G1HeapRegionRemSet::G1HeapRegionRemSet(G1HeapRegion* hr, - G1CardSetConfiguration* config) : +G1HeapRegionRemSet::G1HeapRegionRemSet(G1HeapRegion* hr) : _code_roots(), - _card_set_mm(config, G1CollectedHeap::heap()->card_set_freelist_pool()), - _card_set(new G1CardSet(config, &_card_set_mm)), - _saved_card_set(nullptr), + _cset_group(nullptr), _hr(hr), _state(Untracked) { } +G1HeapRegionRemSet::~G1HeapRegionRemSet() { + assert(!is_added_to_cset_group(), "Still assigned to a CSet group"); +} + void G1HeapRegionRemSet::clear_fcc() { G1FromCardCache::clear(_hr->hrm_index()); } void G1HeapRegionRemSet::clear(bool only_cardset, bool keep_tracked) { - assert(_saved_card_set == nullptr, "pre-condition"); if (!only_cardset) { _code_roots.clear(); } clear_fcc(); - _card_set->clear(); + + if (is_added_to_cset_group()) { + card_set()->clear(); + assert(card_set()->occupied() == 0, "Should be clear."); + } + if (!keep_tracked) { set_state_untracked(); } else { assert(is_tracked(), "must be"); } - assert(occupied() == 0, "Should be clear."); } void G1HeapRegionRemSet::reset_table_scanner() { _code_roots.reset_table_scanner(); - _card_set->reset_table_scanner(); + if (is_added_to_cset_group()) { + card_set()->reset_table_scanner(); + } } G1MonotonicArenaMemoryStats G1HeapRegionRemSet::card_set_memory_stats() const { - return _card_set_mm.memory_stats(); + assert(is_added_to_cset_group(), "pre-condition"); + return cset_group()->card_set_memory_stats(); } void G1HeapRegionRemSet::print_static_mem_size(outputStream* out) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 843eb76bbc9..d2d502636fa 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CardSet.hpp" #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CodeRootSet.hpp" +#include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/g1/g1FromCardCache.hpp" #include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" @@ -35,6 +36,7 @@ #include "utilities/bitMap.hpp" class G1CardSetMemoryManager; +class G1CSetCandidateGroup; class outputStream; class G1HeapRegionRemSet : public CHeapObj { @@ -44,11 +46,8 @@ class G1HeapRegionRemSet : public CHeapObj { // the region that owns this RSet. G1CodeRootSet _code_roots; - G1CardSetMemoryManager _card_set_mm; - - // The set of cards in the Java heap - G1CardSet* _card_set; - G1CardSet* _saved_card_set; + // The collection set groups to which the region owning this RSet is assigned. + G1CSetCandidateGroup* _cset_group; G1HeapRegion* _hr; @@ -57,26 +56,48 @@ class G1HeapRegionRemSet : public CHeapObj { void clear_fcc(); + G1CardSet* card_set() { + assert(is_added_to_cset_group(), "pre-condition"); + return cset_group()->card_set(); + } + + const G1CardSet* card_set() const { + assert(is_added_to_cset_group(), "pre-condition"); + return cset_group()->card_set(); + } + public: - G1HeapRegionRemSet(G1HeapRegion* hr, G1CardSetConfiguration* config); - ~G1HeapRegionRemSet() { delete _card_set; } + G1HeapRegionRemSet(G1HeapRegion* hr); + ~G1HeapRegionRemSet(); bool cardset_is_empty() const { - return _card_set->is_empty(); + return !is_added_to_cset_group() || card_set()->is_empty(); } - void install_group_cardset(G1CardSet* group_cardset) { - assert(group_cardset != nullptr, "pre-condition"); - assert(_saved_card_set == nullptr, "pre-condition"); + void install_cset_group(G1CSetCandidateGroup* cset_group) { + assert(cset_group != nullptr, "pre-condition"); + assert(_cset_group == nullptr, "pre-condition"); - _saved_card_set = _card_set; - _card_set = group_cardset; + _cset_group = cset_group; } - void uninstall_group_cardset(); + void uninstall_cset_group(); - bool has_group_cardset() { - return _saved_card_set != nullptr; + bool is_added_to_cset_group() const { + return _cset_group != nullptr; + } + + G1CSetCandidateGroup* cset_group() { + return _cset_group; + } + + const G1CSetCandidateGroup* cset_group() const { + return _cset_group; + } + + uint cset_group_id() const { + assert(is_added_to_cset_group(), "pre-condition"); + return cset_group()->group_id(); } bool is_empty() const { @@ -84,7 +105,7 @@ public: } bool occupancy_less_or_equal_than(size_t occ) const { - return (code_roots_list_length() == 0) && _card_set->occupancy_less_or_equal_to(occ); + return (code_roots_list_length() == 0) && card_set()->occupancy_less_or_equal_to(occ); } // Iterate the card based remembered set for merging them into the card table. @@ -97,10 +118,10 @@ public: inline static void iterate_for_merge(G1CardSet* card_set, CardOrRangeVisitor& cl); size_t occupied() { - return _card_set->occupied(); + assert(is_added_to_cset_group(), "pre-condition"); + return card_set()->occupied(); } - G1CardSet* card_set() { return _card_set; } static void initialize(MemRegion reserved); @@ -146,13 +167,7 @@ public: // The actual # of bytes this hr_remset takes up. Also includes the code // root set. size_t mem_size() { - return _card_set->mem_size() - + (sizeof(G1HeapRegionRemSet) - sizeof(G1CardSet)) // Avoid double-counting G1CardSet. - + code_roots_mem_size(); - } - - size_t unused_mem_size() { - return _card_set->unused_mem_size(); + return sizeof(G1HeapRegionRemSet) + code_roots_mem_size(); } // Returns the memory occupancy of all static data structures associated diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp index 0df9874e9dd..428586e160a 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp @@ -108,7 +108,7 @@ public: template inline void G1HeapRegionRemSet::iterate_for_merge(CardOrRangeVisitor& cl) { - iterate_for_merge(_card_set, cl); + iterate_for_merge(card_set(), cl); } template @@ -125,6 +125,8 @@ uintptr_t G1HeapRegionRemSet::to_card(OopOrNarrowOopStar from) const { } void G1HeapRegionRemSet::add_reference(OopOrNarrowOopStar from, uint tid) { + assert(is_added_to_cset_group(), "pre-condition"); + assert(_state != Untracked, "must be"); uint cur_idx = _hr->hrm_index(); @@ -137,15 +139,15 @@ void G1HeapRegionRemSet::add_reference(OopOrNarrowOopStar from, uint tid) { return; } - _card_set->add_card(to_card(from)); + card_set()->add_card(to_card(from)); } bool G1HeapRegionRemSet::contains_reference(OopOrNarrowOopStar from) { - return _card_set->contains_card(to_card(from)); + return card_set()->contains_card(to_card(from)); } void G1HeapRegionRemSet::print_info(outputStream* st, OopOrNarrowOopStar from) { - _card_set->print_info(st, to_card(from)); + card_set()->print_info(st, to_card(from)); } #endif // SHARE_VM_GC_G1_G1HEAPREGIONREMSET_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index 5b3bbedfeb2..5ab1f58bce1 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -150,7 +150,11 @@ inline void G1ConcurrentRefineOopClosure::do_oop_work(T* p) { assert(to_rem_set != nullptr, "Need per-region 'into' remsets."); if (to_rem_set->is_tracked()) { - to_rem_set->add_reference(p, _worker_id); + G1HeapRegion* from = _g1h->heap_region_containing(p); + + if (from->rem_set()->cset_group() != to_rem_set->cset_group()) { + to_rem_set->add_reference(p, _worker_id); + } } } @@ -268,7 +272,11 @@ template void G1RebuildRemSetClosure::do_oop_work(T* p) { G1HeapRegion* to = _g1h->heap_region_containing(obj); G1HeapRegionRemSet* rem_set = to->rem_set(); if (rem_set->is_tracked()) { - rem_set->add_reference(p, _worker_id); + G1HeapRegion* from = _g1h->heap_region_containing(p); + + if (from->rem_set()->cset_group() != rem_set->cset_group()) { + rem_set->add_reference(p, _worker_id); + } } } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 4774cbe956d..e8da6c50d87 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -82,7 +82,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _old_gen_is_full(false), _partial_array_splitter(g1h->partial_array_state_manager(), num_workers), _string_dedup_requests(), - _max_num_optional_regions(collection_set->optional_region_length()), + _max_num_optional_regions(collection_set->num_optional_regions()), _numa(g1h->numa()), _obj_alloc_stat(nullptr), ALLOCATION_FAILURE_INJECTOR_ONLY(_allocation_failure_inject_counter(0) COMMA) diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index eee2d02028f..8e81327b42d 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -488,16 +488,15 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, uint min_eden_length, uint max_eden_length) const { uint min_marking_candidates = MIN2(calc_min_old_cset_length(candidates()->last_marking_candidates_length()), - candidates()->marking_regions_length()); + candidates()->from_marking_groups().num_regions()); double predicted_region_evac_time_ms = base_time_ms; - for (G1CollectionSetCandidateInfo* ci : candidates()->marking_regions()) { - // We optimistically assume that any of these marking candidate regions will - // not be pinned, so just consider them as normal. - if (min_marking_candidates == 0) { + uint selected_candidates = 0; + for (G1CSetCandidateGroup* gr : candidates()->from_marking_groups()) { + if (selected_candidates >= min_marking_candidates) { break; } - predicted_region_evac_time_ms += predict_region_total_time_ms(ci->_r, false /* for_young_only_phase */); - min_marking_candidates--; + predicted_region_evac_time_ms += gr->predict_group_total_time_ms(); + selected_candidates += gr->length(); } return calculate_desired_eden_length_before_young_only(predicted_region_evac_time_ms, @@ -523,12 +522,13 @@ double G1Policy::predict_retained_regions_evac_time() const { double result = 0.0; - G1CollectionCandidateList& list = candidates()->retained_regions(); + G1CSetCandidateGroupList* retained_groups = &candidates()->retained_groups(); uint min_regions_left = MIN2(min_retained_old_cset_length(), - list.length()); + retained_groups->num_regions()); - for (G1CollectionSetCandidateInfo* ci : list) { - G1HeapRegion* r = ci->_r; + for (G1CSetCandidateGroup* group : *retained_groups) { + assert(group->length() == 1, "We should only have one region in a retained group"); + G1HeapRegion* r = group->region_at(0); // We only have one region per group. // We optimistically assume that any of these marking candidate regions will // be reclaimable the next gc, so just consider them as normal. if (r->has_pinned_objects()) { @@ -539,12 +539,12 @@ double G1Policy::predict_retained_regions_evac_time() const { break; } min_regions_left--; - result += predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); + result += group->predict_group_total_time_ms(); num_regions++; } log_trace(gc, ergo, heap)("Selected %u of %u retained candidates (pinned %u) taking %1.3fms additional time", - num_regions, list.length(), num_pinned_regions, result); + num_regions, retained_groups->num_regions(), num_pinned_regions, result); return result; } @@ -1111,6 +1111,10 @@ double G1Policy::predict_young_region_other_time_ms(uint count) const { return _analytics->predict_young_other_time_ms(count); } +double G1Policy::predict_non_young_other_time_ms(uint count) const { + return _analytics->predict_non_young_other_time_ms(count); +} + double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) const { if (count == 0) { return 0.0; @@ -1127,13 +1131,12 @@ double G1Policy::predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_on return _analytics->predict_object_copy_time_ms(bytes_to_copy, for_young_only_phase); } -double G1Policy::predict_region_merge_scan_time(G1HeapRegion* hr, bool for_young_only_phase) const { - size_t card_rs_length = hr->rem_set()->occupied(); - size_t scan_card_num = _analytics->predict_scan_card_num(card_rs_length, for_young_only_phase); +double G1Policy::predict_merge_scan_time(size_t card_rs_length) const { + size_t scan_card_num = _analytics->predict_scan_card_num(card_rs_length, false); return - _analytics->predict_card_merge_time_ms(card_rs_length, for_young_only_phase) + - _analytics->predict_card_scan_time_ms(scan_card_num, for_young_only_phase); + _analytics->predict_card_merge_time_ms(card_rs_length, false) + + _analytics->predict_card_scan_time_ms(scan_card_num, false); } double G1Policy::predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_young_only_phase) const { @@ -1143,27 +1146,6 @@ double G1Policy::predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_y _analytics->predict_code_root_scan_time_ms(code_root_length, for_young_only_phase); } -double G1Policy::predict_region_non_copy_time_ms(G1HeapRegion* hr, - bool for_young_only_phase) const { - - double region_elapsed_time_ms = predict_region_merge_scan_time(hr, for_young_only_phase) + - predict_region_code_root_scan_time(hr, for_young_only_phase); - // The prediction of the "other" time for this region is based - // upon the region type and NOT the GC type. - if (hr->is_young()) { - region_elapsed_time_ms += _analytics->predict_young_other_time_ms(1); - } else { - region_elapsed_time_ms += _analytics->predict_non_young_other_time_ms(1); - } - return region_elapsed_time_ms; -} - -double G1Policy::predict_region_total_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const { - return - predict_region_non_copy_time_ms(hr, for_young_only_phase) + - predict_region_copy_time_ms(hr, for_young_only_phase); -} - bool G1Policy::should_allocate_mutator_region() const { uint young_list_length = _g1h->young_regions_count(); return young_list_length < young_list_target_length(); @@ -1339,11 +1321,6 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se } void G1Policy::abandon_collection_set_candidates() { - // Clear remembered sets of remaining candidate regions and the actual candidate - // set. - for (G1HeapRegion* r : *candidates()) { - r->rem_set()->clear(true /* only_cardset */); - } _collection_set->abandon_all_candidates(); } diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 6fcc3ed6b30..ca64f4c8c23 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -46,10 +46,8 @@ class G1HeapRegion; class G1CollectionSet; -class G1CollectionCandidateList; class G1CollectionSetCandidates; class G1CollectionSetChooser; -class G1CollectionCandidateRegionList; class G1IHOPControl; class G1Analytics; class G1SurvivorRegions; @@ -140,7 +138,6 @@ public: double predict_base_time_ms(size_t pending_cards, size_t card_rs_length) const; -private: // Base time contains handling remembered sets and constant other time of the // whole young gen, refinement buffers, and copying survivors. // Basically everything but copying eden regions. @@ -148,24 +145,16 @@ private: // Copy time for a region is copying live data. double predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const; - // Merge-scan time for a region is handling card-based remembered sets of that region - // (as a single unit). - double predict_region_merge_scan_time(G1HeapRegion* hr, bool for_young_only_phase) const; // Code root scan time prediction for the given region. double predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_young_only_phase) const; - // Non-copy time for a region is handling remembered sets and other time. - double predict_region_non_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const; - -public: + double predict_merge_scan_time(size_t card_rs_length) const; // Predict other time for count young regions. double predict_young_region_other_time_ms(uint count) const; + double predict_non_young_other_time_ms(uint count) const; // Predict copying live data time for count eden regions. Return the predict bytes if // bytes_to_copy is non-null. double predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy = nullptr) const; - // Total time for a region is handling remembered sets (as a single unit), copying its live data - // and other time. - double predict_region_total_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const; void cset_regions_freed() { bool update = should_update_surv_rate_group_predictors(); @@ -247,11 +236,11 @@ private: // Limit the given desired young length to available free regions. uint calculate_young_target_length(uint desired_young_length) const; - size_t predict_bytes_to_copy(G1HeapRegion* hr) const; double predict_survivor_regions_evac_time() const; double predict_retained_regions_evac_time() const; public: + size_t predict_bytes_to_copy(G1HeapRegion* hr) const; size_t pending_cards_at_gc_start() const { return _pending_cards_at_gc_start; } // The minimum number of retained regions we will add to the CSet during a young GC. diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 531ca498456..eaf9c229fe6 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -29,6 +29,7 @@ #include "gc/g1/g1CardTable.inline.hpp" #include "gc/g1/g1CardTableEntryClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectionSet.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" #include "gc/g1/g1FromCardCache.hpp" @@ -1118,6 +1119,7 @@ class G1MergeHeapRootsTask : public WorkerTask { // This is needed to be able to use the bitmap for evacuation failure handling. class G1ClearBitmapClosure : public G1HeapRegionClosure { G1CollectedHeap* _g1h; + G1RemSetScanState* _scan_state; void assert_bitmap_clear(G1HeapRegion* hr, const G1CMBitMap* bitmap) { assert(bitmap->get_next_marked_addr(hr->bottom(), hr->end()) == hr->end(), @@ -1142,7 +1144,10 @@ class G1MergeHeapRootsTask : public WorkerTask { } public: - G1ClearBitmapClosure(G1CollectedHeap* g1h) : _g1h(g1h) { } + G1ClearBitmapClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state) : + _g1h(g1h), + _scan_state(scan_state) + { } bool do_heap_region(G1HeapRegion* hr) { assert(_g1h->is_in_cset(hr), "Should only be used iterating the collection set"); @@ -1156,26 +1161,11 @@ class G1MergeHeapRootsTask : public WorkerTask { assert_bitmap_clear(hr, _g1h->concurrent_mark()->mark_bitmap()); } _g1h->concurrent_mark()->clear_statistics(hr); + _scan_state->add_all_dirty_region(hr->hrm_index()); return false; } }; - // Helper to allow two closure to be applied when - // iterating through the collection set. - class G1CombinedClosure : public G1HeapRegionClosure { - G1HeapRegionClosure* _closure1; - G1HeapRegionClosure* _closure2; - public: - G1CombinedClosure(G1HeapRegionClosure* cl1, G1HeapRegionClosure* cl2) : - _closure1(cl1), - _closure2(cl2) { } - - bool do_heap_region(G1HeapRegion* hr) { - return _closure1->do_heap_region(hr) || - _closure2->do_heap_region(hr); - } - }; - // Visitor for the remembered sets of humongous candidate regions to merge their // remembered set into the card table. class G1FlushHumongousCandidateRemSets : public G1HeapRegionIndexClosure { @@ -1394,14 +1384,15 @@ public: { // 2. collection set G1MergeCardSetClosure merge(_scan_state); - G1ClearBitmapClosure clear(g1h); - G1CombinedClosure combined(&merge, &clear); + G1ClearBitmapClosure clear_bitmap(g1h, _scan_state); if (_initial_evacuation) { G1HeapRegionRemSet::iterate_for_merge(g1h->young_regions_cardset(), merge); } - g1h->collection_set_iterate_increment_from(&combined, nullptr, worker_id); + g1h->collection_set()->merge_cardsets_for_collection_groups(merge, worker_id, _num_workers); + + g1h->collection_set_iterate_increment_from(&clear_bitmap, nullptr, worker_id); G1MergeCardSetStats stats = merge.stats(); for (uint i = 0; i < G1GCPhaseTimes::MergeRSContainersSentinel; i++) { @@ -1475,6 +1466,16 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { workers->run_task(&cl, num_workers); } + { + size_t young_rs_length = g1h->young_regions_cardset()->occupied(); + // We only use young_rs_length statistics to estimate young regions length. + g1h->policy()->record_card_rs_length(young_rs_length); + + // Clear current young only collection set. Survivor regions will be added + // to the set during evacuation. + g1h->young_regions_cset_group()->clear(); + } + print_merge_heap_roots_stats(); } diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index adc2be84380..6960191f37c 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -191,6 +191,12 @@ private: size_t _max_rs_mem_sz; G1HeapRegion* _max_rs_mem_sz_region; + size_t _max_code_root_mem_sz; + G1HeapRegion* _max_code_root_mem_sz_region; + + size_t _max_group_cardset_mem_sz; + G1CSetCandidateGroup* _max_cardset_mem_sz_group; + size_t total_rs_unused_mem_sz() const { return _all.rs_unused_mem_size(); } size_t total_rs_mem_sz() const { return _all.rs_mem_size(); } size_t total_cards_occupied() const { return _all.cards_occupied(); } @@ -198,8 +204,8 @@ private: size_t max_rs_mem_sz() const { return _max_rs_mem_sz; } G1HeapRegion* max_rs_mem_sz_region() const { return _max_rs_mem_sz_region; } - size_t _max_code_root_mem_sz; - G1HeapRegion* _max_code_root_mem_sz_region; + size_t max_group_cardset_mem_sz() const { return _max_group_cardset_mem_sz; } + G1CSetCandidateGroup* max_cardset_mem_sz_group() const { return _max_cardset_mem_sz_group; } size_t total_code_root_mem_sz() const { return _all.code_root_mem_size(); } size_t total_code_root_elems() const { return _all.code_root_elems(); } @@ -211,28 +217,29 @@ public: HRRSStatsIter() : _young("Young"), _humongous("Humongous"), _free("Free"), _old("Old"), _all("All"), _max_rs_mem_sz(0), _max_rs_mem_sz_region(nullptr), - _max_code_root_mem_sz(0), _max_code_root_mem_sz_region(nullptr) + _max_code_root_mem_sz(0), _max_code_root_mem_sz_region(nullptr), + _max_group_cardset_mem_sz(0), _max_cardset_mem_sz_group(nullptr) {} bool do_heap_region(G1HeapRegion* r) { G1HeapRegionRemSet* hrrs = r->rem_set(); + size_t rs_mem_sz = 0; + size_t rs_unused_mem_sz = 0; + size_t occupied_cards = 0; - size_t occupied_cards = hrrs->occupied(); - // G1HeapRegionRemSet::mem_size() includes the - // size of the code roots - size_t rs_unused_mem_sz = hrrs->unused_mem_size(); - size_t rs_mem_sz = hrrs->mem_size(); + // Accumulate card set details for regions that are assigned to single region + // groups. G1HeapRegionRemSet::mem_size() includes the size of the code roots + if (hrrs->is_added_to_cset_group() && hrrs->cset_group()->length() == 1) { + G1CardSet* card_set = hrrs->cset_group()->card_set(); - if (r->is_young()) { - uint num_young = G1CollectedHeap::heap()->young_regions_count(); - occupied_cards /= num_young; - rs_unused_mem_sz /= num_young; - rs_mem_sz /= num_young; - } + rs_mem_sz = hrrs->mem_size() + card_set->mem_size(); + rs_unused_mem_sz = card_set->unused_mem_size(); + occupied_cards = hrrs->occupied(); - if (rs_mem_sz > _max_rs_mem_sz) { - _max_rs_mem_sz = rs_mem_sz; - _max_rs_mem_sz_region = r; + if (rs_mem_sz > _max_rs_mem_sz) { + _max_rs_mem_sz = rs_mem_sz; + _max_rs_mem_sz_region = r; + } } size_t code_root_mem_sz = hrrs->code_roots_mem_size(); @@ -262,6 +269,47 @@ public: return false; } + void do_cset_groups() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + G1CSetCandidateGroup* young_only_cset_group = g1h->young_regions_cset_group(); + + // If the group has only a single region, then stats were accumulated + // during region iteration. + if (young_only_cset_group->length() > 1) { + G1CardSet* young_only_card_set = young_only_cset_group->card_set(); + size_t rs_mem_sz = young_only_card_set->mem_size(); + size_t rs_unused_mem_sz = young_only_card_set->unused_mem_size(); + size_t occupied_cards = young_only_card_set->occupied(); + + _max_group_cardset_mem_sz = rs_mem_sz; + _max_cardset_mem_sz_group = young_only_cset_group; + + // Only update cardset details + _young.add(rs_unused_mem_sz, rs_mem_sz, occupied_cards, 0, 0, false); + _all.add(rs_unused_mem_sz, rs_mem_sz, occupied_cards, 0, 0, false); + } + + + RegionTypeCounter* current = &_old; + for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->from_marking_groups()) { + if (group->length() > 1) { + G1CardSet* group_card_set = group->card_set(); + size_t rs_mem_sz = group_card_set->mem_size(); + size_t rs_unused_mem_sz = group_card_set->unused_mem_size(); + size_t occupied_cards = group_card_set->occupied(); + + if (rs_mem_sz > _max_group_cardset_mem_sz) { + _max_group_cardset_mem_sz = rs_mem_sz; + _max_cardset_mem_sz_group = group; + } + + // Only update cardset details + _old.add(rs_unused_mem_sz, rs_mem_sz, occupied_cards, 0, 0, false); + _all.add(rs_unused_mem_sz, rs_mem_sz, occupied_cards, 0, 0, false); + } + } + } + void print_summary_on(outputStream* out) { RegionTypeCounter* counters[] = { &_young, &_humongous, &_free, &_old, nullptr }; @@ -281,13 +329,25 @@ public: (*current)->print_cards_occupied_info_on(out, total_cards_occupied()); } - // Largest sized rem set region statistics - G1HeapRegionRemSet* rem_set = max_rs_mem_sz_region()->rem_set(); - out->print_cr(" Region with largest rem set = " HR_FORMAT ", " - "size = %zu occupied = %zu", - HR_FORMAT_PARAMS(max_rs_mem_sz_region()), - rem_set->mem_size(), - rem_set->occupied()); + // Largest sized single region rem set statistics + if (max_rs_mem_sz_region() != nullptr) { + G1HeapRegionRemSet* rem_set = max_rs_mem_sz_region()->rem_set(); + out->print_cr(" Region with largest rem set = " HR_FORMAT ", " + "size = %zu occupied = %zu", + HR_FORMAT_PARAMS(max_rs_mem_sz_region()), + rem_set->mem_size(), + rem_set->occupied()); + } + + if (max_cardset_mem_sz_group() != nullptr) { + G1CSetCandidateGroup* cset_group = max_cardset_mem_sz_group(); + G1HeapRegionRemSet* rem_set = max_rs_mem_sz_region()->rem_set(); + out->print_cr(" Collectionset Candidate Group with largest cardset = %u:(%u regions), " + "size = %zu occupied = %zu", + cset_group->group_id(), cset_group->length(), + cset_group->card_set()->mem_size(), + cset_group->card_set()->occupied()); + } G1HeapRegionRemSet::print_static_mem_size(out); G1CollectedHeap* g1h = G1CollectedHeap::heap(); @@ -332,5 +392,6 @@ void G1RemSetSummary::print_on(outputStream* out, bool show_thread_times) { HRRSStatsIter blk; G1CollectedHeap::heap()->heap_region_iterate(&blk); + blk.do_cset_groups(); blk.print_summary_on(out); } diff --git a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp index 9874b78699d..a5c8882e057 100644 --- a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp @@ -104,6 +104,17 @@ void G1RemSetTrackingPolicy::update_after_rebuild(G1HeapRegion* r) { r->rem_set()->clear(true /* only_cardset */); }); } + + size_t remset_bytes = r->rem_set()->mem_size(); + size_t occupied = 0; + // per region cardset details only valid if group contains a single region. + if (r->rem_set()->is_added_to_cset_group() && + r->rem_set()->cset_group()->length() == 1 ) { + G1CardSet *card_set = r->rem_set()->cset_group()->card_set(); + remset_bytes += card_set->mem_size(); + occupied = card_set->occupied(); + } + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); log_trace(gc, remset, tracking)("After rebuild region %u " "(tams " PTR_FORMAT " " @@ -113,7 +124,7 @@ void G1RemSetTrackingPolicy::update_after_rebuild(G1HeapRegion* r) { r->hrm_index(), p2i(cm->top_at_mark_start(r)), cm->live_bytes(r->hrm_index()), - r->rem_set()->occupied(), - r->rem_set()->mem_size()); + occupied, + remset_bytes); } } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 9cb734ec653..2223d667998 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -272,7 +272,7 @@ void G1YoungCollector::calculate_collection_set(G1EvacInfo* evacuation_info, dou collection_set()->finalize_initial_collection_set(target_pause_time_ms, survivor_regions()); evacuation_info->set_collection_set_regions(collection_set()->region_length() + - collection_set()->optional_region_length()); + collection_set()->num_optional_regions()); concurrent_mark()->verify_no_collection_set_oops(); @@ -515,7 +515,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) Tickspan task_time = run_task_timed(&g1_prep_task); G1MonotonicArenaMemoryStats sampled_card_set_stats = g1_prep_task.all_card_set_stats(); - sampled_card_set_stats.add(_g1h->young_regions_card_set_mm()->memory_stats()); + sampled_card_set_stats.add(_g1h->young_regions_card_set_memory_stats()); _g1h->set_young_gen_card_set_stats(sampled_card_set_stats); _g1h->set_humongous_stats(g1_prep_task.humongous_total(), g1_prep_task.humongous_candidates()); @@ -790,7 +790,7 @@ void G1YoungCollector::evacuate_next_optional_regions(G1ParScanThreadStateSet* p void G1YoungCollector::evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states) { const double collection_start_time_ms = phase_times()->cur_collection_start_sec() * 1000.0; - while (!evacuation_alloc_failed() && collection_set()->optional_region_length() > 0) { + while (!evacuation_alloc_failed() && collection_set()->num_optional_regions() > 0) { double time_used_ms = os::elapsedTime() * 1000.0 - collection_start_time_ms; double time_left_ms = MaxGCPauseMillis - time_used_ms; @@ -798,7 +798,7 @@ void G1YoungCollector::evacuate_optional_collection_set(G1ParScanThreadStateSet* if (time_left_ms < 0 || !collection_set()->finalize_optional_for_evacuation(time_left_ms * policy()->optional_evacuation_fraction())) { log_trace(gc, ergo, cset)("Skipping evacuation of %u optional regions, no more regions can be evacuated in %.3fms", - collection_set()->optional_region_length(), time_left_ms); + collection_set()->num_optional_regions(), time_left_ms); break; } @@ -989,9 +989,9 @@ void G1YoungCollector::enqueue_candidates_as_root_regions() { assert(collector_state()->in_concurrent_start_gc(), "must be"); G1CollectionSetCandidates* candidates = collection_set()->candidates(); - for (G1HeapRegion* r : *candidates) { + candidates->iterate_regions([&] (G1HeapRegion* r) { _g1h->concurrent_mark()->add_root_region(r); - } + }); } void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, @@ -1111,7 +1111,7 @@ void G1YoungCollector::collect() { collection_set(), &_evac_failure_regions); - bool may_do_optional_evacuation = collection_set()->optional_region_length() != 0; + bool may_do_optional_evacuation = collection_set()->num_optional_regions() != 0; // Actually do the work... evacuate_initial_collection_set(&per_thread_states, may_do_optional_evacuation); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 5b9086be362..4dfa026e420 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -106,8 +106,12 @@ public: G1MonotonicArenaMemoryStats _total; G1CollectionSetCandidates* candidates = g1h->collection_set()->candidates(); - for (G1HeapRegion* r : *candidates) { - _total.add(r->rem_set()->card_set_memory_stats()); + for (G1CSetCandidateGroup* gr : candidates->from_marking_groups()) { + _total.add(gr->card_set_memory_stats()); + } + + for (G1CSetCandidateGroup* gr : candidates->retained_groups()) { + _total.add(gr->card_set_memory_stats()); } g1h->set_collection_set_candidates_stats(_total); } @@ -617,7 +621,6 @@ class FreeCSetStats { size_t _bytes_allocated_in_old_since_last_gc; // Size of young regions turned into old size_t _failure_used_words; // Live size in failed regions size_t _failure_waste_words; // Wasted size in failed regions - size_t _card_rs_length; // (Card Set) Remembered set size uint _regions_freed; // Number of regions freed public: @@ -627,7 +630,6 @@ public: _bytes_allocated_in_old_since_last_gc(0), _failure_used_words(0), _failure_waste_words(0), - _card_rs_length(0), _regions_freed(0) { } void merge_stats(FreeCSetStats* other) { @@ -637,7 +639,6 @@ public: _bytes_allocated_in_old_since_last_gc += other->_bytes_allocated_in_old_since_last_gc; _failure_used_words += other->_failure_used_words; _failure_waste_words += other->_failure_waste_words; - _card_rs_length += other->_card_rs_length; _regions_freed += other->_regions_freed; } @@ -652,10 +653,6 @@ public: G1Policy *policy = g1h->policy(); policy->old_gen_alloc_tracker()->add_allocated_bytes_since_last_gc(_bytes_allocated_in_old_since_last_gc); - // Add the cards from the group cardsets. - _card_rs_length += g1h->young_regions_cardset()->occupied(); - - policy->record_card_rs_length(_card_rs_length); policy->cset_regions_freed(); } @@ -681,10 +678,6 @@ public: _before_used_bytes += used; _regions_freed += 1; } - - void account_card_rs_length(G1HeapRegion* r) { - _card_rs_length += r->rem_set()->occupied(); - } }; // Closure applied to all regions in the collection set. @@ -806,8 +799,6 @@ public: if (r->is_young()) { - // We only use card_rs_length statistics to estimate young regions length. - stats()->account_card_rs_length(r); assert_tracks_surviving_words(r); r->record_surv_words_in_group(_surviving_young_words[r->young_index_in_cset()]); } @@ -894,8 +885,6 @@ public: p->record_serial_free_cset_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); _g1h->clear_collection_set(); - - _g1h->young_regions_cardset()->clear(); } double worker_cost() const override { return G1CollectedHeap::heap()->collection_set()->region_length(); } diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index ed02ba2dc5c..44d0d22257e 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -277,6 +277,14 @@ "as a percentage of the heap size.") \ range(0, 100) \ \ + product(uint, G1OldCSetGroupSize, 5, EXPERIMENTAL, \ + "The maximum number of old CSet regions in a collection group. " \ + "All regions in a group will be evacuated in the same GC pause." \ + "The first group calculated after marking from marking " \ + "candidates may exceed this limit as it is calculated based on " \ + "G1MixedGCCountTarget.") \ + range(1, 256) \ + \ product(bool, G1VerifyHeapRegionCodeRoots, false, DIAGNOSTIC, \ "Verify the code root lists attached to each heap region.") \ \ diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java index 866a7cd583e..5e567fb1a46 100644 --- a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java @@ -46,20 +46,17 @@ class TestResultTracker { private int trackedRegion = -1; private int curGC = -1; private String stdout; - private int expectedMarkingSkipEvents; // How many times has the region from the "marking" collection set candidate set been "skipped". private int expectedRetainedSkipEvents; // How many times has the region from the "retained" collection set candidate set been "skipped". private int expectedDropEvents; // How many times has the region from the "retained" collection set candidate set been "dropped". private int expectedMarkingReclaimEvents; // How many times has the region from the "marking" collection set candidate set been put into the collection set. - private int expectedRetainedReclaimEvents; // How many times has the region from the "marking" collection set candidate set been put into the collection set. + private int expectedRetainedReclaimEvents; // How many times has the region from the "retained" collection set candidate set been put into the collection set. TestResultTracker(String stdout, - int expectedMarkingSkipEvents, int expectedRetainedSkipEvents, int expectedDropEvents, int expectedMarkingReclaimEvents, int expectedRetainedReclaimEvents) { this.stdout = stdout; - this.expectedMarkingSkipEvents = expectedMarkingSkipEvents; this.expectedRetainedSkipEvents = expectedRetainedSkipEvents; this.expectedDropEvents = expectedDropEvents; this.expectedMarkingReclaimEvents = expectedMarkingReclaimEvents; @@ -116,34 +113,12 @@ class TestResultTracker { // The code below tracks that single pinned region through the various stages as defined by the policy. // public void verify() throws Exception { - final String skipDropEvents = "GC\\((\\d+)\\).*(Marking|Retained) candidate (\\d+) can not be reclaimed currently\\. (Skipping|Dropping)"; + final String skipDropEvents = "GC\\((\\d+)\\).*(Retained) candidate (\\d+) can not be reclaimed currently\\. (Skipping|Dropping)"; final String reclaimEvents = "GC\\((\\d+)\\) Finish adding (retained|marking) candidates to collection set\\. Initial: (\\d+).*pinned: (\\d+)"; Matcher skipDropMatcher = Pattern.compile(skipDropEvents, Pattern.MULTILINE).matcher(stdout); Matcher reclaimMatcher = Pattern.compile(reclaimEvents, Pattern.MULTILINE).matcher(stdout); - for (int i = 0; i < expectedMarkingSkipEvents; i++) { - expectMoreMatches(skipDropMatcher, "expectedMarkingSkipEvents"); - curGC = expectIncreasingGC(skipDropMatcher); - - Asserts.assertEQ("Marking", skipDropMatcher.group(2), "Expected \"Marking\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(2) + "\""); - updateOrCompareCurRegion("MarkingSkip", Integer.parseInt(skipDropMatcher.group(3))); - Asserts.assertEQ("Skipping", skipDropMatcher.group(4), "Expected \"Skipping\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(4) + "\""); - - while (true) { - if (!reclaimMatcher.find()) { - Asserts.fail("Could not find \"Finish adding * candidates\" line for GC " + curGC); - } - if (reclaimMatcher.group(2).equals("retained")) { - continue; - } - if (Integer.parseInt(reclaimMatcher.group(1)) == curGC) { - int actual = Integer.parseInt(reclaimMatcher.group(4)); - Asserts.assertEQ(actual, 1, "Expected number of pinned to be 1 after marking skip but is " + actual); - break; - } - } - } for (int i = 0; i < expectedRetainedSkipEvents; i++) { expectMoreMatches(skipDropMatcher, "expectedRetainedSkipEvents"); @@ -232,11 +207,11 @@ class TestResultTracker { public class TestPinnedOldObjectsEvacuation { public static void main(String[] args) throws Exception { - // younGCsBeforeUnpin, expectedMarkingSkipEvents, expectedRetainedSkipEvents, expectedDropEvents, expectedMarkingReclaimEvents, expectedRetainedReclaimEvents - testPinnedEvacuation(1, 1, 0, 0, 0, 1); - testPinnedEvacuation(2, 1, 1, 0, 0, 1); - testPinnedEvacuation(3, 1, 2, 0, 0, 1); - testPinnedEvacuation(4, 1, 2, 1, 0, 0); + // younGCsBeforeUnpin, expectedRetainedSkipEvents, expectedDropEvents, expectedMarkingReclaimEvents, expectedRetainedReclaimEvents + testPinnedEvacuation(1, 0, 0, 0, 1); + testPinnedEvacuation(2, 1, 0, 0, 1); + testPinnedEvacuation(3, 2, 0, 0, 1); + testPinnedEvacuation(4, 2, 1, 0, 0); } private static int numMatches(String stringToMatch, String pattern) { @@ -252,7 +227,6 @@ public class TestPinnedOldObjectsEvacuation { } private static void testPinnedEvacuation(int youngGCsBeforeUnpin, - int expectedMarkingSkipEvents, int expectedRetainedSkipEvents, int expectedDropEvents, int expectedMarkingReclaimEvents, @@ -278,7 +252,6 @@ public class TestPinnedOldObjectsEvacuation { output.shouldHaveExitValue(0); TestResultTracker t = new TestResultTracker(output.getStdout(), - expectedMarkingSkipEvents, expectedRetainedSkipEvents, expectedDropEvents, expectedMarkingReclaimEvents,