< prev index next >

src/share/vm/gc/g1/g1CollectorPolicy.cpp

Print this page
rev 9431 : dihop-changes
rev 9433 : imported patch erik-jmasa-review
rev 9434 : imported patch fix-evac-failure-needs-stats

*** 26,45 **** --- 26,47 ---- #include "gc/g1/concurrentG1Refine.hpp" #include "gc/g1/concurrentMark.hpp" #include "gc/g1/concurrentMarkThread.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorPolicy.hpp" + #include "gc/g1/g1IHOPControl.hpp" #include "gc/g1/g1ErgoVerbose.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1Log.hpp" #include "gc/g1/heapRegion.inline.hpp" #include "gc/g1/heapRegionRemSet.hpp" #include "gc/shared/gcPolicyCounters.hpp" #include "runtime/arguments.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" + #include "utilities/pair.hpp" // Different defaults for different number of GC threads // They were chosen by running GCOld and SPECjbb on debris with different // numbers of GC threads and choosing them based on the results
*** 146,156 **** _recorded_survivor_regions(0), _recorded_survivor_head(NULL), _recorded_survivor_tail(NULL), _survivors_age_table(true), ! _gc_overhead_perc(0.0) { // SurvRateGroups below must be initialized after the predictor because they // indirectly use it through this object passed to their constructor. _short_lived_surv_rate_group = new SurvRateGroup(&_predictor, "Short Lived", G1YoungSurvRateNumRegionsSummary); --- 148,162 ---- _recorded_survivor_regions(0), _recorded_survivor_head(NULL), _recorded_survivor_tail(NULL), _survivors_age_table(true), ! _gc_overhead_perc(0.0), ! ! _last_old_allocated_bytes(0), ! _ihop_control(NULL), ! _initial_mark_to_mixed() { // SurvRateGroups below must be initialized after the predictor because they // indirectly use it through this object passed to their constructor. _short_lived_surv_rate_group = new SurvRateGroup(&_predictor, "Short Lived", G1YoungSurvRateNumRegionsSummary);
*** 286,295 **** --- 292,305 ---- _reserve_regions = 0; _collectionSetChooser = new CollectionSetChooser(); } + G1CollectorPolicy::~G1CollectorPolicy() { + delete _ihop_control; + } + double G1CollectorPolicy::get_new_prediction(TruncatedSeq const* seq) const { return _predictor.get_new_prediction(seq); } void G1CollectorPolicy::initialize_alignments() {
*** 315,324 **** --- 325,336 ---- uintx max_regions = G1CollectedHeap::heap()->max_regions(); size_t max_young_size = (size_t)_young_gen_sizer->max_young_length(max_regions) * HeapRegion::GrainBytes; if (max_young_size != MaxNewSize) { FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size); } + + _ihop_control = create_ihop_control(); } G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); } G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), _adaptive_size(true),
*** 520,548 **** // constraints (i.e., user-defined minimum bound). Currently, we // effectively don't set this bound. return _young_gen_sizer->max_desired_young_length(); } ! void G1CollectorPolicy::update_young_list_max_and_target_length() { ! update_young_list_max_and_target_length(get_new_prediction(_rs_lengths_seq)); } ! void G1CollectorPolicy::update_young_list_max_and_target_length(size_t rs_lengths) { ! update_young_list_target_length(rs_lengths); update_max_gc_locker_expansion(); } ! void G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) { ! _young_list_target_length = bounded_young_list_target_length(rs_lengths); } ! void G1CollectorPolicy::update_young_list_target_length() { ! update_young_list_target_length(get_new_prediction(_rs_lengths_seq)); ! } ! uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths) const { ! // Calculate the absolute and desired min bounds. // This is how many young regions we already have (currently: the survivors). uint base_min_length = recorded_survivor_regions(); uint desired_min_length = calculate_young_list_desired_min_length(base_min_length); // This is the absolute minimum young length. Ensure that we --- 532,561 ---- // constraints (i.e., user-defined minimum bound). Currently, we // effectively don't set this bound. return _young_gen_sizer->max_desired_young_length(); } ! uint G1CollectorPolicy::update_young_list_max_and_target_length() { ! return update_young_list_max_and_target_length(get_new_prediction(_rs_lengths_seq)); } ! uint G1CollectorPolicy::update_young_list_max_and_target_length(size_t rs_lengths) { ! uint unbounded_target_length = update_young_list_target_length(rs_lengths); update_max_gc_locker_expansion(); + return unbounded_target_length; } ! uint G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) { ! YoungTargetLengths young_lengths = young_list_target_lengths(rs_lengths); ! _young_list_target_length = young_lengths.first; ! return young_lengths.second; } ! G1CollectorPolicy::YoungTargetLengths G1CollectorPolicy::young_list_target_lengths(size_t rs_lengths) const { ! YoungTargetLengths result; ! // Calculate the absolute and desired min bounds first. // This is how many young regions we already have (currently: the survivors). uint base_min_length = recorded_survivor_regions(); uint desired_min_length = calculate_young_list_desired_min_length(base_min_length); // This is the absolute minimum young length. Ensure that we
*** 550,568 **** uint absolute_min_length = base_min_length + MAX2(_g1->young_list()->eden_length(), (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. - // 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; - } uint desired_max_length = calculate_young_list_desired_max_length(); - if (desired_max_length > absolute_max_length) { - desired_max_length = absolute_max_length; - } uint young_list_target_length = 0; if (adaptive_young_list_length()) { if (collector_state()->gcs_are_young()) { young_list_target_length = --- 563,573 ----
*** 579,588 **** --- 584,604 ---- // 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;
*** 593,603 **** assert(young_list_target_length > recorded_survivor_regions(), "we should be able to allocate at least one eden region"); assert(young_list_target_length >= absolute_min_length, "post-condition"); ! return young_list_target_length; } uint G1CollectorPolicy::calculate_young_list_target_length(size_t rs_lengths, uint base_min_length, --- 609,620 ---- assert(young_list_target_length > recorded_survivor_regions(), "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 G1CollectorPolicy::calculate_young_list_target_length(size_t rs_lengths, uint base_min_length,
*** 836,845 **** --- 853,866 ---- // Reset survivors SurvRateGroup. _survivor_surv_rate_group->reset(); update_young_list_max_and_target_length(); update_rs_lengths_prediction(); _collectionSetChooser->clear(); + + _last_old_allocated_bytes = 0; + + record_pause(FullGC, _full_collection_start_sec, end_sec); } void G1CollectorPolicy::record_stop_world_start() { _stop_world_start = os::elapsedTime(); }
*** 893,913 **** double elapsed_time_ms = (end_time_sec - _mark_remark_start_sec)*1000.0; _concurrent_mark_remark_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; ! _mmu_tracker->add_pause(_mark_remark_start_sec, end_time_sec); } void G1CollectorPolicy::record_concurrent_mark_cleanup_start() { _mark_cleanup_start_sec = os::elapsedTime(); } void G1CollectorPolicy::record_concurrent_mark_cleanup_completed() { bool should_continue_with_reclaim = next_gc_should_be_mixed("request last young-only gc", "skip last young-only gc"); collector_state()->set_last_young_gc(should_continue_with_reclaim); collector_state()->set_in_marking_window(false); } void G1CollectorPolicy::record_concurrent_pause() { if (_stop_world_start > 0.0) { --- 914,938 ---- double elapsed_time_ms = (end_time_sec - _mark_remark_start_sec)*1000.0; _concurrent_mark_remark_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; ! record_pause(Remark, _mark_remark_start_sec, end_time_sec); } void G1CollectorPolicy::record_concurrent_mark_cleanup_start() { _mark_cleanup_start_sec = os::elapsedTime(); } void G1CollectorPolicy::record_concurrent_mark_cleanup_completed() { bool should_continue_with_reclaim = next_gc_should_be_mixed("request last young-only gc", "skip last young-only gc"); collector_state()->set_last_young_gc(should_continue_with_reclaim); + // We skip the marking phase. + if (!should_continue_with_reclaim) { + abort_time_to_mixed_tracking(); + } collector_state()->set_in_marking_window(false); } void G1CollectorPolicy::record_concurrent_pause() { if (_stop_world_start > 0.0) {
*** 950,965 **** bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc_word_size) { if (about_to_start_mixed_phase()) { return false; } ! size_t marking_initiating_used_threshold = ! (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent; size_t cur_used_bytes = _g1->non_young_capacity_bytes(); size_t alloc_byte_size = alloc_word_size * HeapWordSize; ! if ((cur_used_bytes + alloc_byte_size) > marking_initiating_used_threshold) { if (collector_state()->gcs_are_young() && !collector_state()->last_young_gc()) { ergo_verbose5(ErgoConcCycles, "request concurrent cycle initiation", ergo_format_reason("occupancy higher than threshold") ergo_format_byte("occupancy") --- 975,991 ---- bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc_word_size) { if (about_to_start_mixed_phase()) { return false; } ! size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold(); ! size_t cur_used_bytes = _g1->non_young_capacity_bytes(); size_t alloc_byte_size = alloc_word_size * HeapWordSize; + size_t marking_request_bytes = cur_used_bytes + alloc_byte_size; ! if (marking_request_bytes > marking_initiating_used_threshold) { if (collector_state()->gcs_are_young() && !collector_state()->last_young_gc()) { ergo_verbose5(ErgoConcCycles, "request concurrent cycle initiation", ergo_format_reason("occupancy higher than threshold") ergo_format_byte("occupancy")
*** 967,977 **** ergo_format_byte_perc("threshold") ergo_format_str("source"), cur_used_bytes, alloc_byte_size, marking_initiating_used_threshold, ! (double) InitiatingHeapOccupancyPercent, source); return true; } else { ergo_verbose5(ErgoConcCycles, "do not request concurrent cycle initiation", --- 993,1003 ---- ergo_format_byte_perc("threshold") ergo_format_str("source"), cur_used_bytes, alloc_byte_size, marking_initiating_used_threshold, ! (double) marking_initiating_used_threshold / _g1->capacity() * 100, source); return true; } else { ergo_verbose5(ErgoConcCycles, "do not request concurrent cycle initiation",
*** 996,1007 **** void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t cards_scanned) { double end_time_sec = os::elapsedTime(); assert(_cur_collection_pause_used_regions_at_start >= cset_region_length(), "otherwise, the subtraction below does not make sense"); - size_t rs_size = - _cur_collection_pause_used_regions_at_start - cset_region_length(); size_t cur_used_bytes = _g1->used(); assert(cur_used_bytes == _g1->recalculate_used(), "It should!"); bool last_pause_included_initial_mark = false; bool update_stats = !_g1->evacuation_failed(); --- 1022,1031 ----
*** 1011,1040 **** _short_lived_surv_rate_group->print(); // do that for any other surv rate groups too } #endif // PRODUCT last_pause_included_initial_mark = collector_state()->during_initial_mark_pause(); if (last_pause_included_initial_mark) { record_concurrent_mark_init_end(0.0); } else { maybe_start_marking(); } ! _mmu_tracker->add_pause(end_time_sec - pause_time_ms/1000.0, end_time_sec); ! ! if (update_stats) { ! _trace_young_gen_time_data.record_end_collection(pause_time_ms, phase_times()); ! // this is where we update the allocation rate of the application ! double app_time_ms = ! (phase_times()->cur_collection_start_sec() * 1000.0 - _prev_collection_pause_end_ms); if (app_time_ms < MIN_TIMER_GRANULARITY) { // This usually happens due to the timer not having the required // granularity. Some Linuxes are the usual culprits. // We'll just set it to something (arbitrarily) small. app_time_ms = 1.0; } // We maintain the invariant that all objects allocated by mutator // threads will be allocated out of eden regions. So, we can use // the eden region number allocated since the previous GC to // calculate the application's allocate rate. The only exception // to that is humongous objects that are allocated separately. But --- 1035,1063 ---- _short_lived_surv_rate_group->print(); // do that for any other surv rate groups too } #endif // PRODUCT + record_pause(young_gc_pause_kind(), end_time_sec - pause_time_ms / 1000.0, end_time_sec); + last_pause_included_initial_mark = collector_state()->during_initial_mark_pause(); if (last_pause_included_initial_mark) { record_concurrent_mark_init_end(0.0); } else { maybe_start_marking(); } ! double app_time_ms = (phase_times()->cur_collection_start_sec() * 1000.0 - _prev_collection_pause_end_ms); if (app_time_ms < MIN_TIMER_GRANULARITY) { // This usually happens due to the timer not having the required // granularity. Some Linuxes are the usual culprits. // We'll just set it to something (arbitrarily) small. app_time_ms = 1.0; } + + if (update_stats) { + _trace_young_gen_time_data.record_end_collection(pause_time_ms, phase_times()); // We maintain the invariant that all objects allocated by mutator // threads will be allocated out of eden regions. So, we can use // the eden region number allocated since the previous GC to // calculate the application's allocate rate. The only exception // to that is humongous objects that are allocated separately. But
*** 1075,1093 **** assert(!last_pause_included_initial_mark, "The last young GC is not allowed to be an initial mark GC"); if (next_gc_should_be_mixed("start mixed GCs", "do not start mixed GCs")) { collector_state()->set_gcs_are_young(false); } collector_state()->set_last_young_gc(false); } if (!collector_state()->last_gc_was_young()) { // This is a mixed GC. Here we decide whether to continue doing // mixed GCs or not. - if (!next_gc_should_be_mixed("continue mixed GCs", "do not continue mixed GCs")) { collector_state()->set_gcs_are_young(true); maybe_start_marking(); --- 1098,1118 ---- assert(!last_pause_included_initial_mark, "The last young GC is not allowed to be an initial mark GC"); if (next_gc_should_be_mixed("start mixed GCs", "do not start mixed GCs")) { collector_state()->set_gcs_are_young(false); + } else { + // We aborted the mixed GC phase early. + abort_time_to_mixed_tracking(); } collector_state()->set_last_young_gc(false); } if (!collector_state()->last_gc_was_young()) { // This is a mixed GC. Here we decide whether to continue doing // mixed GCs or not. if (!next_gc_should_be_mixed("continue mixed GCs", "do not continue mixed GCs")) { collector_state()->set_gcs_are_young(true); maybe_start_marking();
*** 1175,1187 **** } collector_state()->set_in_marking_window(new_in_marking_window); collector_state()->set_in_marking_window_im(new_in_marking_window_im); _free_regions_at_end_of_collection = _g1->num_free_regions(); ! update_young_list_max_and_target_length(); update_rs_lengths_prediction(); // Note that _mmu_tracker->max_gc_time() returns the time in seconds. double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSetUpdatingPauseTimePercent / 100.0; double scan_hcc_time_ms = average_time_ms(G1GCPhaseTimes::ScanHCC); --- 1200,1221 ---- } collector_state()->set_in_marking_window(new_in_marking_window); collector_state()->set_in_marking_window_im(new_in_marking_window_im); _free_regions_at_end_of_collection = _g1->num_free_regions(); ! // 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(); update_rs_lengths_prediction(); + update_ihop_prediction(app_time_ms / 1000.0, + _last_old_allocated_bytes, + last_unrestrained_young_length * HeapRegion::GrainBytes); + _last_old_allocated_bytes = 0; + // Note that _mmu_tracker->max_gc_time() returns the time in seconds. double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSetUpdatingPauseTimePercent / 100.0; double scan_hcc_time_ms = average_time_ms(G1GCPhaseTimes::ScanHCC);
*** 1203,1212 **** --- 1237,1293 ---- update_rs_time_goal_ms); _collectionSetChooser->verify(); } + G1IHOPControl* G1CollectorPolicy::create_ihop_control() const { + return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent, + G1CollectedHeap::heap()->max_capacity()); + } + + void G1CollectorPolicy::update_ihop_prediction(double mutator_time_s, + size_t mutator_alloc_bytes, + size_t young_gen_size) { + // 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 + // very high or very low values. They may be caused by e.g. back-to-back gcs. + double const min_valid_time = 1e-6; + + bool report = false; + + double marking_to_mixed_time = -1.0; + if (!collector_state()->last_gc_was_young() && _initial_mark_to_mixed.has_result()) { + marking_to_mixed_time = _initial_mark_to_mixed.last_marking_time(); + assert(marking_to_mixed_time > 0.0, + "Initial mark to mixed time must be larger than zero but is %.3f", + marking_to_mixed_time); + if (marking_to_mixed_time > min_valid_time) { + _ihop_control->update_marking_length(marking_to_mixed_time); + report = true; + } + } + + // 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 (collector_state()->last_gc_was_young() && mutator_time_s > min_valid_time) { + _ihop_control->update_allocation_info(mutator_time_s, mutator_alloc_bytes, young_gen_size); + report = true; + } + + if (report) { + report_ihop_statistics(); + } + } + + void G1CollectorPolicy::report_ihop_statistics() { + _ihop_control->print(); + } + #define EXT_SIZE_FORMAT "%.1f%s" #define EXT_SIZE_PARAMS(bytes) \ byte_size_in_proper_unit((double)(bytes)), \ proper_unit_for_byte_size((bytes))
*** 1715,1726 **** const uint overpartition_factor = 4; const uint min_chunk_size = MAX2(n_regions / n_workers, 1U); return MAX2(n_regions / (n_workers * overpartition_factor), min_chunk_size); } ! void ! G1CollectorPolicy::record_concurrent_mark_cleanup_end() { _collectionSetChooser->clear(); WorkGang* workers = _g1->workers(); uint n_workers = workers->active_workers(); --- 1796,1806 ---- const uint overpartition_factor = 4; const uint min_chunk_size = MAX2(n_regions / n_workers, 1U); return MAX2(n_regions / (n_workers * overpartition_factor), min_chunk_size); } ! void G1CollectorPolicy::record_concurrent_mark_cleanup_end() { _collectionSetChooser->clear(); WorkGang* workers = _g1->workers(); uint n_workers = workers->active_workers();
*** 1735,1745 **** double end_sec = os::elapsedTime(); double elapsed_time_ms = (end_sec - _mark_cleanup_start_sec) * 1000.0; _concurrent_mark_cleanup_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; ! _mmu_tracker->add_pause(_mark_cleanup_start_sec, end_sec); } // Add the heap region at the head of the non-incremental collection set void G1CollectorPolicy::add_old_region_to_cset(HeapRegion* hr) { assert(_inc_cset_build_state == Active, "Precondition"); --- 1815,1826 ---- double end_sec = os::elapsedTime(); double elapsed_time_ms = (end_sec - _mark_cleanup_start_sec) * 1000.0; _concurrent_mark_cleanup_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; ! ! record_pause(Cleanup, _mark_cleanup_start_sec, end_sec); } // Add the heap region at the head of the non-incremental collection set void G1CollectorPolicy::add_old_region_to_cset(HeapRegion* hr) { assert(_inc_cset_build_state == Active, "Precondition");
*** 1951,1960 **** --- 2032,2094 ---- // this pause we decided to postpone it. That's OK. collector_state()->set_initiate_conc_mark_if_possible(true); } } + G1CollectorPolicy::PauseKind G1CollectorPolicy::young_gc_pause_kind() const { + assert(!collector_state()->full_collection(), "must be"); + if (collector_state()->during_initial_mark_pause()) { + assert(collector_state()->last_gc_was_young(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return InitialMarkGC; + } else if (collector_state()->last_young_gc()) { + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(collector_state()->last_gc_was_young(), "must be"); + return LastYoungGC; + } else if (!collector_state()->last_gc_was_young()) { + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return MixedGC; + } else { + assert(collector_state()->last_gc_was_young(), "must be"); + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return YoungOnlyGC; + } + } + + void G1CollectorPolicy::record_pause(PauseKind kind, double start, double end) { + // Manage the MMU tracker. For some reason it ignores Full GCs. + if (kind != FullGC) { + _mmu_tracker->add_pause(start, end); + } + // Manage the mutator time tracking from initial mark to first mixed gc. + switch (kind) { + case FullGC: + abort_time_to_mixed_tracking(); + break; + case Cleanup: + case Remark: + case YoungOnlyGC: + case LastYoungGC: + _initial_mark_to_mixed.add_pause(end - start); + break; + case InitialMarkGC: + _initial_mark_to_mixed.record_initial_mark_end(end); + break; + case MixedGC: + _initial_mark_to_mixed.record_mixed_gc_start(start); + break; + default: + ShouldNotReachHere(); + } + } + + void G1CollectorPolicy::abort_time_to_mixed_tracking() { + _initial_mark_to_mixed.reset(); + } + bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, const char* false_action_str) const { CollectionSetChooser* cset_chooser = _collectionSetChooser; if (cset_chooser->is_empty()) { ergo_verbose0(ErgoMixedGCs,
< prev index next >