< prev index next >
src/hotspot/share/gc/g1/g1Policy.cpp
Print this page
rev 60300 : 8249676: [REDO] G1 incorrectly limiting young gen size when using the reserve can result in repeated full gcs
Summary: Reapply JDK-8244603 after fixing JDK-8249192.
Reviewed-by:
*** 44,53 ****
--- 44,54 ----
#include "gc/g1/heapRegionRemSet.hpp"
#include "gc/shared/concurrentGCBreakpoints.hpp"
#include "gc/shared/gcPolicyCounters.hpp"
#include "logging/log.hpp"
#include "runtime/arguments.hpp"
+ #include "runtime/globals.hpp"
#include "runtime/java.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/debug.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/pair.hpp"
*** 59,70 ****
_mmu_tracker(new G1MMUTrackerQueue(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)),
_ihop_control(create_ihop_control(&_predictor)),
_policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)),
_full_collection_start_sec(0.0),
_collection_pause_end_millis(os::javaTimeNanos() / NANOSECS_PER_MILLISEC),
_young_list_target_length(0),
- _young_list_fixed_length(0),
_young_list_max_length(0),
_eden_surv_rate_group(new G1SurvRateGroup()),
_survivor_surv_rate_group(new G1SurvRateGroup()),
_reserve_factor((double) G1ReservePercent / 100.0),
_reserve_regions(0),
--- 60,71 ----
_mmu_tracker(new G1MMUTrackerQueue(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)),
_ihop_control(create_ihop_control(&_predictor)),
_policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)),
_full_collection_start_sec(0.0),
_collection_pause_end_millis(os::javaTimeNanos() / NANOSECS_PER_MILLISEC),
+ _young_list_desired_length(0),
_young_list_target_length(0),
_young_list_max_length(0),
_eden_surv_rate_group(new G1SurvRateGroup()),
_survivor_surv_rate_group(new G1SurvRateGroup()),
_reserve_factor((double) G1ReservePercent / 100.0),
_reserve_regions(0),
*** 106,123 ****
_g1h = g1h;
_collection_set = collection_set;
assert(Heap_lock->owned_by_self(), "Locking discipline.");
- if (!use_adaptive_young_list_length()) {
- _young_list_fixed_length = _young_gen_sizer->min_desired_young_length();
- }
_young_gen_sizer->adjust_max_new_size(_g1h->max_expandable_regions());
_free_regions_at_end_of_collection = _g1h->num_free_regions();
! update_young_list_max_and_target_length();
// We may immediately start allocating regions and placing them on the
// collection set list. Initialize the per-collection set info
_collection_set->start_incremental_building();
}
--- 107,121 ----
_g1h = g1h;
_collection_set = collection_set;
assert(Heap_lock->owned_by_self(), "Locking discipline.");
_young_gen_sizer->adjust_max_new_size(_g1h->max_expandable_regions());
_free_regions_at_end_of_collection = _g1h->num_free_regions();
! update_young_length_bounds();
// We may immediately start allocating regions and placing them on the
// collection set list. Initialize the per-collection set info
_collection_set->start_incremental_building();
}
*** 188,349 ****
_young_gen_sizer->heap_size_changed(new_number_of_regions);
_ihop_control->update_target_occupancy(new_number_of_regions * HeapRegion::GrainBytes);
}
! uint G1Policy::calculate_young_list_desired_min_length(uint base_min_length) const {
uint desired_min_length = 0;
if (use_adaptive_young_list_length()) {
- if (_analytics->num_alloc_rate_ms() > 3) {
double now_sec = os::elapsedTime();
double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0;
double alloc_rate_ms = _analytics->predict_alloc_rate_ms();
desired_min_length = (uint) ceil(alloc_rate_ms * when_ms);
- } else {
- // otherwise we don't have enough info to make the prediction
- }
}
! desired_min_length += base_min_length;
! // make sure we don't go below any user-defined minimum bound
! return MAX2(_young_gen_sizer->min_desired_young_length(), desired_min_length);
! }
!
! uint G1Policy::calculate_young_list_desired_max_length() const {
! // Here, we might want to also take into account any additional
! // constraints (i.e., user-defined minimum bound). Currently, we
! // effectively don't set this bound.
! return _young_gen_sizer->max_desired_young_length();
! }
!
! uint G1Policy::update_young_list_max_and_target_length() {
! return update_young_list_max_and_target_length(_analytics->predict_rs_length());
! }
!
! uint G1Policy::update_young_list_max_and_target_length(size_t rs_length) {
! uint unbounded_target_length = update_young_list_target_length(rs_length);
! update_max_gc_locker_expansion();
! return unbounded_target_length;
}
! uint G1Policy::update_young_list_target_length(size_t rs_length) {
! YoungTargetLengths young_lengths = young_list_target_lengths(rs_length);
! _young_list_target_length = young_lengths.first;
!
! return young_lengths.second;
}
! G1Policy::YoungTargetLengths G1Policy::young_list_target_lengths(size_t rs_length) const {
! YoungTargetLengths result;
// Calculate the absolute and desired min bounds first.
! // This is how many young regions we already have (currently: the survivors).
! const uint base_min_length = _g1h->survivor_regions_count();
! uint desired_min_length = calculate_young_list_desired_min_length(base_min_length);
! // This is the absolute minimum young length. Ensure that we
! // will at least have one eden region available for allocation.
! uint absolute_min_length = base_min_length + MAX2(_g1h->eden_regions_count(), (uint)1);
! // If we shrank the young list target it should not shrink below the current size.
! desired_min_length = MAX2(desired_min_length, absolute_min_length);
! // Calculate the absolute and desired max bounds.
!
! uint desired_max_length = calculate_young_list_desired_max_length();
! uint young_list_target_length = 0;
if (use_adaptive_young_list_length()) {
! if (collector_state()->in_young_only_phase()) {
! young_list_target_length =
! calculate_young_list_target_length(rs_length,
! base_min_length,
! desired_min_length,
! desired_max_length);
} else {
! // Don't calculate anything and let the code below bound it to
! // the desired_min_length, i.e., do the next GC as soon as
! // possible to maximize how many old regions we can add to it.
! }
} else {
// The user asked for a fixed young gen so we'll fix the young gen
// whether the next GC is young or mixed.
! young_list_target_length = _young_list_fixed_length;
}
! result.second = young_list_target_length;
!
! // We will try our best not to "eat" into the reserve.
! uint absolute_max_length = 0;
! if (_free_regions_at_end_of_collection > _reserve_regions) {
! absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions;
}
- if (desired_max_length > absolute_max_length) {
- desired_max_length = absolute_max_length;
}
! // Make sure we don't go over the desired max length, nor under the
! // desired min length. In case they clash, desired_min_length wins
! // which is why that test is second.
! if (young_list_target_length > desired_max_length) {
! young_list_target_length = desired_max_length;
! }
! if (young_list_target_length < desired_min_length) {
! young_list_target_length = desired_min_length;
! }
! assert(young_list_target_length > base_min_length,
! "we should be able to allocate at least one eden region");
! assert(young_list_target_length >= absolute_min_length, "post-condition");
! result.first = young_list_target_length;
! return result;
}
! uint G1Policy::calculate_young_list_target_length(size_t rs_length,
! uint base_min_length,
! uint desired_min_length,
! uint desired_max_length) const {
assert(use_adaptive_young_list_length(), "pre-condition");
- assert(collector_state()->in_young_only_phase(), "only call this for young GCs");
! // In case some edge-condition makes the desired max length too small...
! if (desired_max_length <= desired_min_length) {
! return desired_min_length;
! }
!
! // We'll adjust min_young_length and max_young_length not to include
! // the already allocated young regions (i.e., so they reflect the
! // min and max eden regions we'll allocate). The base_min_length
! // will be reflected in the predictions by the
! // survivor_regions_evac_time prediction.
! assert(desired_min_length > base_min_length, "invariant");
! uint min_young_length = desired_min_length - base_min_length;
! assert(desired_max_length > base_min_length, "invariant");
! uint max_young_length = desired_max_length - base_min_length;
!
! const double target_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0;
! const size_t pending_cards = _analytics->predict_pending_cards();
! const double base_time_ms = predict_base_elapsed_time_ms(pending_cards, rs_length);
! const uint available_free_regions = _free_regions_at_end_of_collection;
! const uint base_free_regions =
! available_free_regions > _reserve_regions ? available_free_regions - _reserve_regions : 0;
// Here, we will make sure that the shortest young length that
// makes sense fits within the target pause time.
G1YoungLengthPredictor p(base_time_ms,
! base_free_regions,
! target_pause_time_ms,
this);
! if (p.will_fit(min_young_length)) {
// The shortest young length will fit into the target pause time;
// we'll now check whether the absolute maximum number of young
// regions will fit in the target pause time. If not, we'll do
// a binary search between min_young_length and max_young_length.
! if (p.will_fit(max_young_length)) {
// The maximum young length will fit into the target pause time.
// We are done so set min young length to the maximum length (as
// the result is assumed to be returned in min_young_length).
! min_young_length = max_young_length;
} else {
// The maximum possible number of young regions will not fit within
// the target pause time so we'll search for the optimal
// length. The loop invariants are:
//
--- 186,443 ----
_young_gen_sizer->heap_size_changed(new_number_of_regions);
_ihop_control->update_target_occupancy(new_number_of_regions * HeapRegion::GrainBytes);
}
! uint G1Policy::calculate_desired_eden_length_by_mmu() const {
! // One could argue that any useful eden length to keep any MMU would be 1, but
! // in theory this is possible. Other constraints enforce a minimum eden of 1
! // anyway.
uint desired_min_length = 0;
if (use_adaptive_young_list_length()) {
double now_sec = os::elapsedTime();
double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0;
double alloc_rate_ms = _analytics->predict_alloc_rate_ms();
desired_min_length = (uint) ceil(alloc_rate_ms * when_ms);
}
! return desired_min_length;
}
! void G1Policy::update_young_length_bounds() {
! update_young_length_bounds(_analytics->predict_rs_length());
}
! void G1Policy::update_young_length_bounds(size_t rs_length) {
! _young_list_desired_length = calculate_young_desired_length(rs_length);
! _young_list_target_length = calculate_young_target_length(_young_list_desired_length);
! _young_list_max_length = calculate_young_max_length(_young_list_target_length);
!
! log_debug(gc,ergo,heap)("Young list lengths: desired: %u, target: %u, max: %u",
! _young_list_desired_length,
! _young_list_target_length,
! _young_list_max_length);
! }
!
! // Calculates desired young gen length. It is calculated from:
! //
! // - sizer min/max bounds on young gen
! // - pause time goal for whole young gen evacuation
! // - MMU goal influencing eden to make GCs spaced apart.
! // - a minimum one eden region length.
! //
! // We may enter with already allocated eden and survivor regions, that may be
! // higher than the maximum, or the above goals may result in a desired value
! // smaller than are already allocated.
! // The main reason is revising young length, with our without the GCLocker being
! // active.
! //
! uint G1Policy::calculate_young_desired_length(size_t rs_length) const {
! uint min_young_length_by_sizer = _young_gen_sizer->min_desired_young_length();
! uint max_young_length_by_sizer = _young_gen_sizer->max_desired_young_length();
!
! assert(min_young_length_by_sizer >= 1, "invariant");
! assert(max_young_length_by_sizer >= min_young_length_by_sizer, "invariant");
!
! // Absolute minimum eden length.
! // Enforcing a minimum eden length helps at startup when the predictors are not
! // yet trained on the application to avoid unnecessary (but very short) full gcs
! // on very small (initial) heaps.
! uint const MinDesiredEdenLength = 1;
// Calculate the absolute and desired min bounds first.
! // This is how many survivor regions we already have.
! const uint survivor_length = _g1h->survivor_regions_count();
! // Size of the already allocated young gen.
! const uint allocated_young_length = _g1h->young_regions_count();
! // This is the absolute minimum young length that we can return. Ensure that we
! // don't go below any user-defined minimum bound; but we might have already
! // allocated more than that for reasons. In this case, use that.
! uint absolute_min_young_length = MAX2(allocated_young_length, min_young_length_by_sizer);
! // Calculate the absolute max bounds. After evac failure or when revising the
! // young length we might have exceeded absolute min length or absolute_max_length,
! // so adjust the result accordingly.
! uint absolute_max_young_length = MAX2(max_young_length_by_sizer, absolute_min_young_length);
!
! uint desired_eden_length_by_mmu = 0;
! uint desired_eden_length_by_pause = 0;
! uint desired_eden_length_before_mixed = 0;
! uint desired_young_length = 0;
if (use_adaptive_young_list_length()) {
! desired_eden_length_by_mmu = calculate_desired_eden_length_by_mmu();
!
! const size_t pending_cards = _analytics->predict_pending_cards();
! double survivor_base_time_ms = predict_base_elapsed_time_ms(pending_cards, rs_length);
!
! if (!next_gc_should_be_mixed(NULL, NULL)) {
! desired_eden_length_by_pause =
! calculate_desired_eden_length_by_pause(survivor_base_time_ms,
! absolute_min_young_length - survivor_length,
! absolute_max_young_length - survivor_length);
} else {
! desired_eden_length_before_mixed =
! calculate_desired_eden_length_before_mixed(survivor_base_time_ms,
! absolute_min_young_length - survivor_length,
! absolute_max_young_length - survivor_length);
! }
! // Above either sets desired_eden_length_by_pause or desired_eden_length_before_mixed,
! // the other is zero. Use the one that has been set below.
! uint desired_eden_length = MAX2(desired_eden_length_by_pause,
! desired_eden_length_before_mixed);
!
! // Finally incorporate MMU concerns; assume that it overrides the pause time
! // goal, as the default value has been chosen to effectively disable it.
! // Also request at least one eden region, see above for reasons.
! desired_eden_length = MAX3(desired_eden_length,
! desired_eden_length_by_mmu,
! MinDesiredEdenLength);
!
! desired_young_length = desired_eden_length + survivor_length;
} else {
// The user asked for a fixed young gen so we'll fix the young gen
// whether the next GC is young or mixed.
! desired_young_length = min_young_length_by_sizer;
}
+ // Clamp to absolute min/max after we determined desired lengths.
+ desired_young_length = clamp(desired_young_length, absolute_min_young_length, absolute_max_young_length);
! log_trace(gc, ergo, heap)("Young desired length %u "
! "survivor length %u "
! "allocated young length %u "
! "absolute min young length %u "
! "absolute max young length %u "
! "desired eden length by mmu %u "
! "desired eden length by pause %u "
! "desired eden length before mixed %u"
! "desired eden length by default %u",
! desired_young_length, survivor_length,
! allocated_young_length, absolute_min_young_length,
! absolute_max_young_length, desired_eden_length_by_mmu,
! desired_eden_length_by_pause,
! desired_eden_length_before_mixed,
! MinDesiredEdenLength);
!
! assert(desired_young_length >= allocated_young_length, "must be");
! return desired_young_length;
! }
!
! // Limit the desired (wished) young length by current free regions. If the request
! // can be satisfied without using up reserve regions, do so, otherwise eat into
! // the reserve, giving away at most what the heap sizer allows.
! uint G1Policy::calculate_young_target_length(uint desired_young_length) const {
! uint allocated_young_length = _g1h->young_regions_count();
!
! uint receiving_additional_eden;
! if (allocated_young_length >= desired_young_length) {
! // Already used up all we actually want (may happen as G1 revises the
! // young list length concurrently, or caused by gclocker). Do not allow more,
! // potentially resulting in GC.
! receiving_additional_eden = 0;
! log_trace(gc, ergo, heap)("Young target length: Already used up desired young %u allocated %u",
! desired_young_length,
! allocated_young_length);
! } else {
! // Now look at how many free regions are there currently, and the heap reserve.
! // We will try our best not to "eat" into the reserve as long as we can. If we
! // do, we at most eat the sizer's minimum regions into the reserve or half the
! // reserve rounded up (if possible; this is an arbitrary value).
!
! uint max_to_eat_into_reserve = MIN2(_young_gen_sizer->min_desired_young_length(),
! (_reserve_regions + 1) / 2);
!
! log_trace(gc, ergo, heap)("Young target length: Common "
! "free regions at end of collection %u "
! "desired young length %u "
! "reserve region %u "
! "max to eat into reserve %u",
! _free_regions_at_end_of_collection,
! desired_young_length,
! _reserve_regions,
! max_to_eat_into_reserve);
!
! if (_free_regions_at_end_of_collection <= _reserve_regions) {
! // Fully eat (or already eating) into the reserve, hand back at most absolute_min_length regions.
! uint receiving_young = MIN3(_free_regions_at_end_of_collection,
! desired_young_length,
! max_to_eat_into_reserve);
! // We could already have allocated more regions than what we could get
! // above.
! receiving_additional_eden = allocated_young_length < receiving_young ?
! receiving_young - allocated_young_length : 0;
!
! log_trace(gc, ergo, heap)("Young target length: Fully eat into reserve "
! "receiving young %u receiving additional eden %u",
! receiving_young,
! receiving_additional_eden);
! } else if (_free_regions_at_end_of_collection < (desired_young_length + _reserve_regions)) {
! // Partially eat into the reserve, at most max_to_eat_into_reserve regions.
! uint free_outside_reserve = _free_regions_at_end_of_collection - _reserve_regions;
! assert(free_outside_reserve < desired_young_length,
! "must be %u %u",
! free_outside_reserve, desired_young_length);
!
! uint receiving_within_reserve = MIN2(desired_young_length - free_outside_reserve,
! max_to_eat_into_reserve);
! uint receiving_young = free_outside_reserve + receiving_within_reserve;
! // Again, we could have already allocated more than we could get.
! receiving_additional_eden = allocated_young_length < receiving_young ?
! receiving_young - allocated_young_length : 0;
!
! log_trace(gc, ergo, heap)("Young target length: Partially eat into reserve "
! "free outside reserve %u "
! "receiving within reserve %u "
! "receiving young %u "
! "receiving additional eden %u",
! free_outside_reserve, receiving_within_reserve,
! receiving_young, receiving_additional_eden);
! } else {
! // No need to use the reserve.
! receiving_additional_eden = desired_young_length - allocated_young_length;
! log_trace(gc, ergo, heap)("Young target length: No need to use reserve "
! "receiving additional eden %u",
! receiving_additional_eden);
}
}
! uint target_young_length = allocated_young_length + receiving_additional_eden;
! assert(target_young_length >= allocated_young_length, "must be");
! log_trace(gc, ergo, heap)("Young target length: "
! "young target length %u "
! "allocated young length %u "
! "received additional eden %u",
! target_young_length, allocated_young_length,
! receiving_additional_eden);
! return target_young_length;
}
! uint G1Policy::calculate_desired_eden_length_by_pause(double base_time_ms,
! uint min_eden_length,
! uint max_eden_length) const {
assert(use_adaptive_young_list_length(), "pre-condition");
! assert(min_eden_length <= max_eden_length, "must be %u %u", min_eden_length, max_eden_length);
// Here, we will make sure that the shortest young length that
// makes sense fits within the target pause time.
G1YoungLengthPredictor p(base_time_ms,
! _free_regions_at_end_of_collection,
! _mmu_tracker->max_gc_time() * 1000.0,
this);
! if (p.will_fit(min_eden_length)) {
// The shortest young length will fit into the target pause time;
// we'll now check whether the absolute maximum number of young
// regions will fit in the target pause time. If not, we'll do
// a binary search between min_young_length and max_young_length.
! if (p.will_fit(max_eden_length)) {
// The maximum young length will fit into the target pause time.
// We are done so set min young length to the maximum length (as
// the result is assumed to be returned in min_young_length).
! min_eden_length = max_eden_length;
} else {
// The maximum possible number of young regions will not fit within
// the target pause time so we'll search for the optimal
// length. The loop invariants are:
//
*** 356,396 ****
// the middle value between min_young_length and
// max_young_length fits into the target pause time. If it
// does, it becomes the new min. If it doesn't, it becomes
// the new max. This way we maintain the loop invariants.
! assert(min_young_length < max_young_length, "invariant");
! uint diff = (max_young_length - min_young_length) / 2;
while (diff > 0) {
! uint young_length = min_young_length + diff;
! if (p.will_fit(young_length)) {
! min_young_length = young_length;
} else {
! max_young_length = young_length;
}
! assert(min_young_length < max_young_length, "invariant");
! diff = (max_young_length - min_young_length) / 2;
}
// The results is min_young_length which, according to the
// loop invariants, should fit within the target pause time.
// These are the post-conditions of the binary search above:
! assert(min_young_length < max_young_length,
! "otherwise we should have discovered that max_young_length "
"fits into the pause target and not done the binary search");
! assert(p.will_fit(min_young_length),
! "min_young_length, the result of the binary search, should "
"fit into the pause target");
! assert(!p.will_fit(min_young_length + 1),
! "min_young_length, the result of the binary search, should be "
"optimal, so no larger length should fit into the pause target");
}
} else {
// Even the minimum length doesn't fit into the pause time
// target, return it as the result nevertheless.
}
! return base_min_length + min_young_length;
}
double G1Policy::predict_survivor_regions_evac_time() const {
double survivor_regions_evac_time = 0.0;
const GrowableArray<HeapRegion*>* survivor_regions = _g1h->survivor()->regions();
--- 450,509 ----
// the middle value between min_young_length and
// max_young_length fits into the target pause time. If it
// does, it becomes the new min. If it doesn't, it becomes
// the new max. This way we maintain the loop invariants.
! assert(min_eden_length < max_eden_length, "invariant");
! uint diff = (max_eden_length - min_eden_length) / 2;
while (diff > 0) {
! uint eden_length = min_eden_length + diff;
! if (p.will_fit(eden_length)) {
! min_eden_length = eden_length;
} else {
! max_eden_length = eden_length;
}
! assert(min_eden_length < max_eden_length, "invariant");
! diff = (max_eden_length - min_eden_length) / 2;
}
// The results is min_young_length which, according to the
// loop invariants, should fit within the target pause time.
// These are the post-conditions of the binary search above:
! assert(min_eden_length < max_eden_length,
! "otherwise we should have discovered that max_eden_length "
"fits into the pause target and not done the binary search");
! assert(p.will_fit(min_eden_length),
! "min_eden_length, the result of the binary search, should "
"fit into the pause target");
! assert(!p.will_fit(min_eden_length + 1),
! "min_eden_length, the result of the binary search, should be "
"optimal, so no larger length should fit into the pause target");
}
} else {
// Even the minimum length doesn't fit into the pause time
// target, return it as the result nevertheless.
}
! return min_eden_length;
! }
!
! uint G1Policy::calculate_desired_eden_length_before_mixed(double survivor_base_time_ms,
! uint min_eden_length,
! uint max_eden_length) const {
! G1CollectionSetCandidates* candidates = _collection_set->candidates();
!
! uint min_old_regions_end = MIN2(candidates->cur_idx() + calc_min_old_cset_length(), candidates->num_regions());
! double predicted_region_evac_time_ms = survivor_base_time_ms;
! for (uint i = candidates->cur_idx(); i < min_old_regions_end; i++) {
! HeapRegion* r = candidates->at(i);
! predicted_region_evac_time_ms += predict_region_total_time_ms(r, false);
! }
! uint desired_eden_length_by_min_cset_length =
! calculate_desired_eden_length_by_pause(predicted_region_evac_time_ms,
! min_eden_length,
! max_eden_length);
!
! return desired_eden_length_by_min_cset_length;
}
double G1Policy::predict_survivor_regions_evac_time() const {
double survivor_regions_evac_time = 0.0;
const GrowableArray<HeapRegion*>* survivor_regions = _g1h->survivor()->regions();
*** 416,427 ****
if (rs_length > _rs_length_prediction) {
// add 10% to avoid having to recalculate often
size_t rs_length_prediction = rs_length * 1100 / 1000;
update_rs_length_prediction(rs_length_prediction);
!
! update_young_list_max_and_target_length(rs_length_prediction);
}
}
void G1Policy::update_rs_length_prediction() {
update_rs_length_prediction(_analytics->predict_rs_length());
--- 529,539 ----
if (rs_length > _rs_length_prediction) {
// add 10% to avoid having to recalculate often
size_t rs_length_prediction = rs_length * 1100 / 1000;
update_rs_length_prediction(rs_length_prediction);
! update_young_length_bounds(rs_length_prediction);
}
}
void G1Policy::update_rs_length_prediction() {
update_rs_length_prediction(_analytics->predict_rs_length());
*** 465,475 ****
_eden_surv_rate_group->start_adding_regions();
// also call this on any additional surv rate groups
_free_regions_at_end_of_collection = _g1h->num_free_regions();
_survivor_surv_rate_group->reset();
! update_young_list_max_and_target_length();
update_rs_length_prediction();
_old_gen_alloc_tracker.reset_after_full_gc();
record_pause(FullGC, _full_collection_start_sec, end_sec);
--- 577,587 ----
_eden_surv_rate_group->start_adding_regions();
// also call this on any additional surv rate groups
_free_regions_at_end_of_collection = _g1h->num_free_regions();
_survivor_surv_rate_group->reset();
! update_young_length_bounds();
update_rs_length_prediction();
_old_gen_alloc_tracker.reset_after_full_gc();
record_pause(FullGC, _full_collection_start_sec, end_sec);
*** 799,818 ****
update_rs_length_prediction();
// Do not update dynamic IHOP due to G1 periodic collection as it is highly likely
// that in this case we are not running in a "normal" operating mode.
if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) {
! // IHOP control wants to know the expected young gen length if it were not
! // restrained by the heap reserve. Using the actual length would make the
! // prediction too small and the limit the young gen every time we get to the
! // predicted target occupancy.
! size_t last_unrestrained_young_length = update_young_list_max_and_target_length();
_old_gen_alloc_tracker.reset_after_young_gc(app_time_ms / 1000.0);
update_ihop_prediction(_old_gen_alloc_tracker.last_cycle_duration(),
_old_gen_alloc_tracker.last_cycle_old_bytes(),
- last_unrestrained_young_length * HeapRegion::GrainBytes,
is_young_only_pause(this_pause));
_ihop_control->send_trace_event(_g1h->gc_tracer_stw());
} else {
// Any garbage collection triggered as periodic collection resets the time-to-mixed
--- 911,925 ----
update_rs_length_prediction();
// Do not update dynamic IHOP due to G1 periodic collection as it is highly likely
// that in this case we are not running in a "normal" operating mode.
if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) {
! update_young_length_bounds();
_old_gen_alloc_tracker.reset_after_young_gc(app_time_ms / 1000.0);
update_ihop_prediction(_old_gen_alloc_tracker.last_cycle_duration(),
_old_gen_alloc_tracker.last_cycle_old_bytes(),
is_young_only_pause(this_pause));
_ihop_control->send_trace_event(_g1h->gc_tracer_stw());
} else {
// Any garbage collection triggered as periodic collection resets the time-to-mixed
*** 858,868 ****
}
}
void G1Policy::update_ihop_prediction(double mutator_time_s,
size_t mutator_alloc_bytes,
- size_t young_gen_size,
bool this_gc_was_young_only) {
// Always try to update IHOP prediction. Even evacuation failures give information
// about e.g. whether to start IHOP earlier next time.
// Avoid using really small application times that might create samples with
--- 965,974 ----
*** 886,895 ****
--- 992,1006 ----
// As an approximation for the young gc promotion rates during marking we use
// all of them. In many applications there are only a few if any young gcs during
// marking, which makes any prediction useless. This increases the accuracy of the
// prediction.
if (this_gc_was_young_only && mutator_time_s > min_valid_time) {
+ // IHOP control wants to know the expected young gen length if it were not
+ // restrained by the heap reserve. Using the actual length would make the
+ // prediction too small and the limit the young gen every time we get to the
+ // predicted target occupancy.
+ size_t young_gen_size = young_list_desired_length() * HeapRegion::GrainBytes;
_ihop_control->update_allocation_info(mutator_time_s, mutator_alloc_bytes, young_gen_size);
report = true;
}
if (report) {
*** 992,1014 ****
void G1Policy::print_age_table() {
_survivors_age_table.print_age_table(_tenuring_threshold);
}
! void G1Policy::update_max_gc_locker_expansion() {
uint expansion_region_num = 0;
if (GCLockerEdenExpansionPercent > 0) {
double perc = (double) GCLockerEdenExpansionPercent / 100.0;
double expansion_region_num_d = perc * (double) _young_list_target_length;
// We use ceiling so that if expansion_region_num_d is > 0.0 (but
// less than 1.0) we'll get 1.
expansion_region_num = (uint) ceil(expansion_region_num_d);
} else {
assert(expansion_region_num == 0, "sanity");
}
! _young_list_max_length = _young_list_target_length + expansion_region_num;
! assert(_young_list_target_length <= _young_list_max_length, "post-condition");
}
// Calculates survivor space parameters.
void G1Policy::update_survivors_policy() {
double max_survivor_regions_d =
--- 1103,1126 ----
void G1Policy::print_age_table() {
_survivors_age_table.print_age_table(_tenuring_threshold);
}
! uint G1Policy::calculate_young_max_length(uint target_young_length) const {
uint expansion_region_num = 0;
if (GCLockerEdenExpansionPercent > 0) {
double perc = (double) GCLockerEdenExpansionPercent / 100.0;
double expansion_region_num_d = perc * (double) _young_list_target_length;
// We use ceiling so that if expansion_region_num_d is > 0.0 (but
// less than 1.0) we'll get 1.
expansion_region_num = (uint) ceil(expansion_region_num_d);
} else {
assert(expansion_region_num == 0, "sanity");
}
! uint max_length = target_young_length + expansion_region_num;
! assert(target_young_length <= max_length, "post-condition");
! return max_length;
}
// Calculates survivor space parameters.
void G1Policy::update_survivors_policy() {
double max_survivor_regions_d =
*** 1239,1264 ****
bool G1Policy::next_gc_should_be_mixed(const char* true_action_str,
const char* false_action_str) const {
G1CollectionSetCandidates* candidates = _collection_set->candidates();
! if (candidates->is_empty()) {
log_debug(gc, ergo)("%s (candidate old regions not available)", false_action_str);
return false;
}
// Is the amount of uncollected reclaimable space above G1HeapWastePercent?
size_t reclaimable_bytes = candidates->remaining_reclaimable_bytes();
double reclaimable_percent = reclaimable_bytes_percent(reclaimable_bytes);
double threshold = (double) G1HeapWastePercent;
if (reclaimable_percent <= threshold) {
log_debug(gc, ergo)("%s (reclaimable percentage not over threshold). candidate old regions: %u reclaimable: " SIZE_FORMAT " (%1.2f) threshold: " UINTX_FORMAT,
false_action_str, candidates->num_remaining(), reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
return false;
}
log_debug(gc, ergo)("%s (candidate old regions available). candidate old regions: %u reclaimable: " SIZE_FORMAT " (%1.2f) threshold: " UINTX_FORMAT,
true_action_str, candidates->num_remaining(), reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
return true;
}
uint G1Policy::calc_min_old_cset_length() const {
// The min old CSet region bound is based on the maximum desired
--- 1351,1382 ----
bool G1Policy::next_gc_should_be_mixed(const char* true_action_str,
const char* false_action_str) const {
G1CollectionSetCandidates* candidates = _collection_set->candidates();
! if (candidates == NULL || candidates->is_empty()) {
! if (false_action_str != NULL) {
log_debug(gc, ergo)("%s (candidate old regions not available)", false_action_str);
+ }
return false;
}
// Is the amount of uncollected reclaimable space above G1HeapWastePercent?
size_t reclaimable_bytes = candidates->remaining_reclaimable_bytes();
double reclaimable_percent = reclaimable_bytes_percent(reclaimable_bytes);
double threshold = (double) G1HeapWastePercent;
if (reclaimable_percent <= threshold) {
+ if (false_action_str != NULL) {
log_debug(gc, ergo)("%s (reclaimable percentage not over threshold). candidate old regions: %u reclaimable: " SIZE_FORMAT " (%1.2f) threshold: " UINTX_FORMAT,
false_action_str, candidates->num_remaining(), reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
+ }
return false;
}
+ if (true_action_str != NULL) {
log_debug(gc, ergo)("%s (candidate old regions available). candidate old regions: %u reclaimable: " SIZE_FORMAT " (%1.2f) threshold: " UINTX_FORMAT,
true_action_str, candidates->num_remaining(), reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
+ }
return true;
}
uint G1Policy::calc_min_old_cset_length() const {
// The min old CSet region bound is based on the maximum desired
< prev index next >