8343782: G1: Use one G1CardSet instance for multiple old gen regions

Reviewed-by: ayang, tschatzl
This commit is contained in:
Ivan Walulya 2025-02-07 10:22:23 +00:00
parent 006ed5c02b
commit 86cec4ea2c
32 changed files with 1071 additions and 762 deletions

View File

@ -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 */);
}

View File

@ -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();
}

View File

@ -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();

View File

@ -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());

View File

@ -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<int>(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();
}

View File

@ -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 <class CardOrRangeVisitor>
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;

View File

@ -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 <class CardOrRangeVisitor>
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 */

View File

@ -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<G1CollectionSetCandidateInfo> 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<G1CollectionSetCandidateInfo> 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<G1CSetCandidateGroup*> 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");

View File

@ -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<G1HeapRegion*>;
// A set of G1HeapRegion*, a thin wrapper around GrowableArray.
class G1CollectionCandidateRegionList {
GrowableArray<G1HeapRegion*> _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<mtGC> {
friend class G1CollectionCandidateListIterator;
using G1CSetCandidateGroupIterator = GrowableArrayIterator<G1CollectionSetCandidateInfo>;
// 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<mtGCCardSet>{
GrowableArray<G1CollectionSetCandidateInfo> _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<G1CSetCandidateGroup*>;
public:
G1CollectionSetCandidatesIterator(G1CollectionSetCandidates* which, uint position);
class G1CSetCandidateGroupList {
GrowableArray<G1CSetCandidateGroup*> _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<G1CSetCandidateGroup*>* groups() {
return &_groups;
}
void verify() const PRODUCT_RETURN;
G1CSetCandidateGroupListIterator begin() const {
return _groups.begin();
}
G1CSetCandidateGroupListIterator end() const {
return _groups.end();
}
template<typename Func>
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<mtGC> {
friend class G1CollectionSetCandidatesIterator;
enum class CandidateOrigin : uint8_t {
Invalid,
@ -181,10 +211,12 @@ class G1CollectionSetCandidates : public CHeapObj<mtGC> {
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<typename Func>
void iterate_regions(Func&& f) const;
};
#endif /* SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP */

View File

@ -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<typename Func>
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<typename Func>
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 */

View File

@ -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");
}

View File

@ -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");
}
}

View File

@ -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.

View File

@ -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);

View File

@ -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)) {

View File

@ -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() ?

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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<mtGC> {
@ -44,11 +46,8 @@ class G1HeapRegionRemSet : public CHeapObj<mtGC> {
// 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<mtGC> {
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

View File

@ -108,7 +108,7 @@ public:
template <class CardOrRangeVisitor>
inline void G1HeapRegionRemSet::iterate_for_merge(CardOrRangeVisitor& cl) {
iterate_for_merge(_card_set, cl);
iterate_for_merge(card_set(), cl);
}
template <class CardOrRangeVisitor>
@ -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

View File

@ -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 <class T> 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);
}
}
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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.

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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(); }

View File

@ -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.") \
\

View File

@ -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,