diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 9a5e390f6f1..f04658a1415 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -353,6 +353,14 @@ size_t G1CollectedHeap::humongous_obj_size_in_regions(size_t word_size) { return align_up(word_size, G1HeapRegion::GrainWords) / G1HeapRegion::GrainWords; } +size_t G1CollectedHeap::allocation_used_bytes(size_t allocation_word_size) { + if (is_humongous(allocation_word_size)) { + return humongous_obj_size_in_regions(allocation_word_size) * G1HeapRegion::GrainBytes; + } else { + return allocation_word_size * HeapWordSize; + } +} + // If could fit into free regions w/o expansion, try. // Otherwise, if can expand, do so. // Otherwise, if using ex regions might help, try with ex given back. @@ -2955,6 +2963,15 @@ void G1CollectedHeap::abandon_collection_set() { collection_set()->abandon(); } +size_t G1CollectedHeap::non_young_occupancy_after_allocation(size_t allocation_word_size) { + // For simplicity, just count whole regions. + const size_t cur_occupancy = (old_regions_count() + humongous_regions_count()) * G1HeapRegion::GrainBytes; + // Humongous allocations will always be assigned to non-young heap, so consider + // that allocation in the result as well. Otherwise the allocation will always + // be in young gen, so there is no need to account it here. + return cur_occupancy + (is_humongous(allocation_word_size) ? allocation_used_bytes(allocation_word_size) : 0); +} + bool G1CollectedHeap::is_old_gc_alloc_region(G1HeapRegion* hr) { return _allocator->is_retained_old_region(hr); } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7a0edfacd0f..5dccf41e909 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -1032,9 +1032,10 @@ public: inline void old_set_add(G1HeapRegion* hr); inline void old_set_remove(G1HeapRegion* hr); - size_t non_young_capacity_bytes() { - return (old_regions_count() + humongous_regions_count()) * G1HeapRegion::GrainBytes; - } + // Returns how much memory there is assigned to non-young heap that can not be + // allocated into any more without garbage collection after a hypothetical + // allocation of allocation_word_size. + size_t non_young_occupancy_after_allocation(size_t allocation_word_size); // Determine whether the given region is one that we are using as an // old GC alloc region. @@ -1226,6 +1227,10 @@ public: // requires. static size_t humongous_obj_size_in_regions(size_t word_size); + // Returns how much space in bytes an allocation of word_size will use up in the + // heap. + static size_t allocation_used_bytes(size_t word_size); + // Print the maximum heap capacity. size_t max_capacity() const override; size_t min_capacity() const; diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp index 03a1444f8e6..4dd0a509bcd 100644 --- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp @@ -366,28 +366,24 @@ static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio) { } size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand, size_t allocation_word_size) { - // If the full collection was triggered by an allocation failure, we should account - // for the bytes required for this allocation under used_after_gc. This prevents - // unnecessary shrinking that would be followed by an expand call to satisfy the - // allocation. - size_t allocation_bytes = allocation_word_size * HeapWordSize; - if (_g1h->is_humongous(allocation_word_size)) { - // Humongous objects are allocated in entire regions, we must calculate - // required space in terms of full regions, not just the object size. - allocation_bytes = G1HeapRegion::align_up_to_region_byte_size(allocation_bytes); - } - + const size_t capacity_after_gc = _g1h->capacity(); // Capacity, free and used after the GC counted as full regions to // include the waste in the following calculations. - const size_t capacity_after_gc = _g1h->capacity(); - const size_t used_after_gc = capacity_after_gc + allocation_bytes - - _g1h->unused_committed_regions_in_bytes() - - // Discount space used by current Eden to establish a - // situation during Remark similar to at the end of full - // GC where eden is empty. During Remark there can be an - // arbitrary number of eden regions which would skew the - // results. - _g1h->eden_regions_count() * G1HeapRegion::GrainBytes; + const size_t current_used_after_gc = capacity_after_gc - + _g1h->unused_committed_regions_in_bytes() - + // Discount space used by current Eden to establish a + // situation during Remark similar to at the end of full + // GC where eden is empty. During Remark there can be an + // arbitrary number of eden regions which would skew the + // results. + _g1h->eden_regions_count() * G1HeapRegion::GrainBytes; + + // Add pending allocation; + const size_t used_after_gc = current_used_after_gc + + // If the full collection was triggered by an allocation failure, + // account that allocation too. Otherwise we could shrink and then + // expand immediately to satisfy the allocation. + _g1h->allocation_used_bytes(allocation_word_size); size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio); size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio); diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.cpp b/src/hotspot/share/gc/g1/g1IHOPControl.cpp index 5c05169c29d..34c8cd0366b 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.cpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.cpp @@ -44,32 +44,37 @@ void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) { _target_occupancy = new_target_occupancy; } +void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_occupancy) { + print_log(non_young_occupancy); + send_trace_event(new_tracer, non_young_occupancy); +} + void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) { assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); _last_allocation_time_s = allocation_time_s; } -void G1IHOPControl::print() { +void G1IHOPControl::print_log(size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); - log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, current occupancy: %zuB, " + log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, " "recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", cur_conc_mark_start_threshold, percent_of(cur_conc_mark_start_threshold, _target_occupancy), _target_occupancy, - G1CollectedHeap::heap()->used(), + non_young_occupancy, _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s * 1000.0, _last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0, last_marking_length_s() * 1000.0); } -void G1IHOPControl::send_trace_event(G1NewTracer* tracer) { +void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), _target_occupancy, - G1CollectedHeap::heap()->used(), + non_young_occupancy, _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s, last_marking_length_s()); @@ -165,27 +170,27 @@ void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) { _marking_times_s.add(marking_length_s); } -void G1AdaptiveIHOPControl::print() { - G1IHOPControl::print(); - size_t actual_target = actual_target_threshold(); - log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target occupancy: %zuB, " - "occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, " +void G1AdaptiveIHOPControl::print_log(size_t non_young_occupancy) { + G1IHOPControl::print_log(non_young_occupancy); + size_t actual_threshold = actual_target_threshold(); + log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, " + "non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, " "predicted marking phase length: %1.2fms, prediction active: %s", get_conc_mark_start_threshold(), - percent_of(get_conc_mark_start_threshold(), actual_target), - actual_target, - G1CollectedHeap::heap()->used(), + percent_of(get_conc_mark_start_threshold(), actual_threshold), + actual_threshold, + non_young_occupancy, _last_unrestrained_young_size, predict(&_allocation_rate_s), predict(&_marking_times_s) * 1000.0, have_enough_data_for_prediction() ? "true" : "false"); } -void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer) { - G1IHOPControl::send_trace_event(tracer); +void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { + G1IHOPControl::send_trace_event(tracer, non_young_occupancy); tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), actual_target_threshold(), - G1CollectedHeap::heap()->used(), + non_young_occupancy, _last_unrestrained_young_size, predict(&_allocation_rate_s), predict(&_marking_times_s), diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.hpp b/src/hotspot/share/gc/g1/g1IHOPControl.hpp index 507fbb269d1..392a12a785a 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.hpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.hpp @@ -55,7 +55,11 @@ class G1IHOPControl : public CHeapObj { // Most recent time from the end of the concurrent start to the start of the first // mixed gc. virtual double last_marking_length_s() const = 0; - public: + + virtual void print_log(size_t non_young_occupancy); + virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); + +public: virtual ~G1IHOPControl() { } // Get the current non-young occupancy at which concurrent marking should start. @@ -76,8 +80,7 @@ class G1IHOPControl : public CHeapObj { // the first mixed gc. virtual void update_marking_length(double marking_length_s) = 0; - virtual void print(); - virtual void send_trace_event(G1NewTracer* tracer); + void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy); }; // The returned concurrent mark starting occupancy threshold is a fixed value @@ -139,6 +142,10 @@ class G1AdaptiveIHOPControl : public G1IHOPControl { double last_mutator_period_old_allocation_rate() const; protected: virtual double last_marking_length_s() const { return _marking_times_s.last(); } + + virtual void print_log(size_t non_young_occupancy); + virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); + public: G1AdaptiveIHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker, @@ -150,9 +157,6 @@ class G1AdaptiveIHOPControl : public G1IHOPControl { virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); virtual void update_marking_length(double marking_length_s); - - virtual void print(); - virtual void send_trace_event(G1NewTracer* tracer); }; #endif // SHARE_GC_G1_G1IHOPCONTROL_HPP diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index ddcdfb0c3a4..19573e11cd7 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -749,22 +749,14 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor } size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold(); - - size_t cur_used_bytes = _g1h->non_young_capacity_bytes(); - size_t allocation_byte_size = allocation_word_size * HeapWordSize; - // For humongous allocations, we need to consider that we actually use full regions - // for allocations. So compare the threshold to this size. - if (_g1h->is_humongous(allocation_word_size)) { - allocation_byte_size = G1HeapRegion::align_up_to_region_byte_size(allocation_byte_size); - } - size_t marking_request_bytes = cur_used_bytes + allocation_byte_size; + size_t non_young_occupancy = _g1h->non_young_occupancy_after_allocation(allocation_word_size); bool result = false; - if (marking_request_bytes > marking_initiating_used_threshold) { + if (non_young_occupancy > marking_initiating_used_threshold) { result = collector_state()->in_young_only_phase(); - log_debug(gc, ergo, ihop)("%s occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", + log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)", - cur_used_bytes, allocation_byte_size, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); + non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); } return result; } @@ -995,10 +987,10 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar update_young_length_bounds(); _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); - update_ihop_prediction(app_time_ms / 1000.0, - G1GCPauseTypeHelper::is_young_only_pause(this_pause)); - - _ihop_control->send_trace_event(_g1h->gc_tracer_stw()); + if (update_ihop_prediction(app_time_ms / 1000.0, + G1GCPauseTypeHelper::is_young_only_pause(this_pause))) { + _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); + } } else { // Any garbage collection triggered as periodic collection resets the time-to-mixed // measurement. Periodic collection typically means that the application is "inactive", i.e. @@ -1045,7 +1037,7 @@ G1IHOPControl* G1Policy::create_ihop_control(const G1OldGenAllocationTracker* ol } } -void G1Policy::update_ihop_prediction(double mutator_time_s, +bool G1Policy::update_ihop_prediction(double mutator_time_s, 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. @@ -1082,13 +1074,7 @@ void G1Policy::update_ihop_prediction(double mutator_time_s, report = true; } - if (report) { - report_ihop_statistics(); - } -} - -void G1Policy::report_ihop_statistics() { - _ihop_control->print(); + return report; } void G1Policy::record_young_gc_pause_end(bool evacuation_failed) { diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 93724657235..72fdc6deb5b 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -60,10 +60,10 @@ class G1Policy: public CHeapObj { static G1IHOPControl* create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker, const G1Predictions* predictor); - // Update the IHOP control with necessary statistics. - void update_ihop_prediction(double mutator_time_s, + // Update the IHOP control with the necessary statistics. Returns true if there + // has been a significant update to the prediction. + bool update_ihop_prediction(double mutator_time_s, bool this_gc_was_young_only); - void report_ihop_statistics(); G1Predictions _predictor; G1Analytics* _analytics; diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index 6c9a87e4d98..ed6a91f41ed 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -98,13 +98,13 @@ void G1NewTracer::report_evacuation_statistics(const G1EvacSummary& young_summar void G1NewTracer::report_basic_ihop_statistics(size_t threshold, size_t target_ccupancy, - size_t current_occupancy, + size_t non_young_occupancy, size_t last_allocation_size, double last_allocation_duration, double last_marking_length) { send_basic_ihop_statistics(threshold, target_ccupancy, - current_occupancy, + non_young_occupancy, last_allocation_size, last_allocation_duration, last_marking_length); @@ -206,7 +206,7 @@ void G1NewTracer::send_old_evacuation_statistics(const G1EvacSummary& summary) c void G1NewTracer::send_basic_ihop_statistics(size_t threshold, size_t target_occupancy, - size_t current_occupancy, + size_t non_young_occupancy, size_t last_allocation_size, double last_allocation_duration, double last_marking_length) { @@ -216,7 +216,7 @@ void G1NewTracer::send_basic_ihop_statistics(size_t threshold, evt.set_threshold(threshold); evt.set_targetOccupancy(target_occupancy); evt.set_thresholdPercentage(target_occupancy > 0 ? ((double)threshold / target_occupancy) : 0.0); - evt.set_currentOccupancy(current_occupancy); + evt.set_currentOccupancy(non_young_occupancy); evt.set_recentMutatorAllocationSize(last_allocation_size); evt.set_recentMutatorDuration(last_allocation_duration * MILLIUNITS); evt.set_recentAllocationRate(last_allocation_duration != 0.0 ? last_allocation_size / last_allocation_duration : 0.0); diff --git a/src/hotspot/share/gc/g1/g1Trace.hpp b/src/hotspot/share/gc/g1/g1Trace.hpp index 025a1c3fe95..1415828f376 100644 --- a/src/hotspot/share/gc/g1/g1Trace.hpp +++ b/src/hotspot/share/gc/g1/g1Trace.hpp @@ -73,13 +73,13 @@ private: void send_basic_ihop_statistics(size_t threshold, size_t target_occupancy, - size_t current_occupancy, + size_t non_young_occupancy, size_t last_allocation_size, double last_allocation_duration, double last_marking_length); void send_adaptive_ihop_statistics(size_t threshold, size_t internal_target_occupancy, - size_t current_occupancy, + size_t non_young_occupancy, size_t additional_buffer_size, double predicted_allocation_rate, double predicted_marking_length,