--- old/src/share/vm/gc/g1/g1CollectedHeap.cpp 2015-11-16 16:53:01.863324036 +0100 +++ new/src/share/vm/gc/g1/g1CollectedHeap.cpp 2015-11-16 16:53:01.774321359 +0100 @@ -5635,6 +5635,14 @@ cur->set_young_index_in_cset(-1); } cur->set_evacuation_failed(false); + // When moving a young gen region to old gen, we "allocate" that whole region + // there. This is in addition to any already evacuated objects. Notify the + // policy about that. + // Old gen regions do not cause an additional allocation: both the objects + // still in the region and the ones already moved are accounted for elsewhere. + if (cur->is_young()) { + policy->add_last_old_allocated_bytes(HeapRegion::GrainBytes); + } // The region is now considered to be old. cur->set_old(); // Do some allocation statistics accounting. Regions that failed evacuation --- old/src/share/vm/gc/g1/g1CollectedHeap.hpp 2015-11-16 16:53:02.396340074 +0100 +++ new/src/share/vm/gc/g1/g1CollectedHeap.hpp 2015-11-16 16:53:02.309337456 +0100 @@ -1353,7 +1353,7 @@ } // Returns the number of regions the humongous object of the given word size - // covers. + // requires. static size_t humongous_obj_size_in_regions(size_t word_size); // Update mod union table with the set of dirty cards. --- old/src/share/vm/gc/g1/g1CollectorPolicy.cpp 2015-11-16 16:53:02.894355058 +0100 +++ new/src/share/vm/gc/g1/g1CollectorPolicy.cpp 2015-11-16 16:53:02.804352350 +0100 @@ -39,6 +39,7 @@ #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 @@ -294,9 +295,7 @@ } G1CollectorPolicy::~G1CollectorPolicy() { - if (_ihop_control != NULL) { - delete _ihop_control; - } + delete _ihop_control; } double G1CollectorPolicy::get_new_prediction(TruncatedSeq const* seq) const { @@ -535,25 +534,26 @@ return _young_gen_sizer->max_desired_young_length(); } -void G1CollectorPolicy::update_young_list_max_and_target_length(size_t* unbounded_target_length) { - update_young_list_max_and_target_length(get_new_prediction(_rs_lengths_seq), unbounded_target_length); +uint G1CollectorPolicy::update_young_list_max_and_target_length() { + return 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, size_t* unbounded_target_length) { - update_young_list_target_length(rs_lengths, unbounded_target_length); +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; } -void G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths, size_t* unbounded_target_length) { - _young_list_target_length = bounded_young_list_target_length(rs_lengths, 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; } -void G1CollectorPolicy::update_young_list_target_length() { - update_young_list_target_length(get_new_prediction(_rs_lengths_seq)); -} +G1CollectorPolicy::YoungTargetLengths G1CollectorPolicy::young_list_target_lengths(size_t rs_lengths) const { + YoungTargetLengths result; -uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths, size_t* unbounded_target_length) const { - // Calculate the absolute and desired min bounds. + // 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(); @@ -586,9 +586,7 @@ young_list_target_length = _young_list_fixed_length; } - if (unbounded_target_length != NULL) { - *unbounded_target_length = young_list_target_length; - } + result.second = young_list_target_length; // We will try our best not to "eat" into the reserve. uint absolute_max_length = 0; @@ -613,7 +611,8 @@ "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; + result.first = young_list_target_length; + return result; } uint @@ -928,7 +927,7 @@ 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 abort the marking phase. + // We skip the marking phase. if (!should_continue_with_reclaim) { abort_time_to_mixed_tracking(); } @@ -1047,19 +1046,16 @@ maybe_start_marking(); } - double app_time_ms = 1.0; + 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()); - // this is where we update the allocation rate of the application - 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 @@ -1210,19 +1206,19 @@ // 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 = 0; - update_young_list_max_and_target_length(&last_unrestrained_young_length); + size_t last_unrestrained_young_length = update_young_list_max_and_target_length(); update_rs_lengths_prediction(); - 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); - } // Only update IHOP information on regular GCs. if (update_stats) { + 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); + } + update_ihop_statistics(marking_to_mixed_time, app_time_ms / 1000.0, _last_old_allocated_bytes, @@ -1271,7 +1267,7 @@ double const min_valid_time = 1e-6; if (marking_time > min_valid_time) { - _ihop_control->update_time_to_mixed(marking_time); + _ihop_control->update_marking_length(marking_time); report = true; } --- old/src/share/vm/gc/g1/g1CollectorPolicy.hpp 2015-11-16 16:53:03.433371276 +0100 +++ new/src/share/vm/gc/g1/g1CollectorPolicy.hpp 2015-11-16 16:53:03.343368568 +0100 @@ -29,9 +29,11 @@ #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1InCSetState.hpp" +#include "gc/g1/g1InitialMarkToMixedTimeTracker.hpp" #include "gc/g1/g1MMUTracker.hpp" #include "gc/g1/g1Predictions.hpp" #include "gc/shared/collectorPolicy.hpp" +#include "utilities/pair.hpp" // A G1CollectorPolicy makes policy decisions that determine the // characteristics of the collector. Examples include: @@ -285,57 +287,7 @@ // young GC phase. size_t _last_old_allocated_bytes; - // Used to track time from the end of initial mark to the first mixed GC. - class InitialMarkToMixedTimeTracker { - private: - bool _active; - double _initial_mark_end_time; - double _mixed_start_time; - double _total_pause_time; - - double wall_time() const { - return _mixed_start_time - _initial_mark_end_time; - } - public: - InitialMarkToMixedTimeTracker() { reset(); } - - void record_initial_mark_end(double end_time) { - assert(!_active, "Initial mark out of order."); - _initial_mark_end_time = end_time; - _active = true; - } - - void record_mixed_gc_start(double start_time) { - if (_active) { - _mixed_start_time = start_time; - _active = false; - } - } - - double last_marking_time() { - assert(has_result(), "Do not have all measurements yet."); - double result = (_mixed_start_time - _initial_mark_end_time) - _total_pause_time; - reset(); - return result; - } - - void reset() { - _active = false; - _total_pause_time = 0.0; - _initial_mark_end_time = -1.0; - _mixed_start_time = -1.0; - } - - void add_pause(double time) { - if (_active) { - _total_pause_time += time; - } - } - - bool has_result() const { return _mixed_start_time > 0.0 && _initial_mark_end_time > 0.0; } - }; - - InitialMarkToMixedTimeTracker _initial_mark_to_mixed; + G1InitialMarkToMixedTimeTracker _initial_mark_to_mixed; public: const G1Predictions& predictor() const { return _predictor; } @@ -541,16 +493,18 @@ double _mark_remark_start_sec; double _mark_cleanup_start_sec; - void update_young_list_max_and_target_length(size_t* unbounded_target_length = NULL); - void update_young_list_max_and_target_length(size_t rs_lengths, size_t* unbounded_target_length = NULL); + // Updates the internal young list maximum and target lengths. Returns the + // unbounded young list target length. + uint update_young_list_max_and_target_length(); + uint update_young_list_max_and_target_length(size_t rs_lengths); // Update the young list target length either by setting it to the // desired fixed value or by calculating it using G1's pause // prediction model. If no rs_lengths parameter is passed, predict // the RS lengths using the prediction model, otherwise use the // given rs_lengths as the prediction. - void update_young_list_target_length(); - void update_young_list_target_length(size_t rs_lengths, size_t* unbounded_target_length = NULL); + // Returns the unbounded young list target length. + uint update_young_list_target_length(size_t rs_lengths); // Calculate and return the minimum desired young list target // length. This is the minimum desired young list length according @@ -573,7 +527,10 @@ uint desired_min_length, uint desired_max_length) const; - uint bounded_young_list_target_length(size_t rs_lengths, size_t* unbounded_target_length = NULL) const; + // Result of the bounded_young_list_target_length() method, containing both the + // bounded as well as the unbounded young list target lengths in this order. + typedef Pair YoungTargetLengths; + YoungTargetLengths young_list_target_lengths(size_t rs_lengths) const; void update_rs_lengths_prediction(); void update_rs_lengths_prediction(size_t prediction); --- old/src/share/vm/gc/g1/g1IHOPControl.cpp 2015-11-16 16:53:03.959387103 +0100 +++ new/src/share/vm/gc/g1/g1IHOPControl.cpp 2015-11-16 16:53:03.872384485 +0100 @@ -26,12 +26,11 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ErgoVerbose.hpp" #include "gc/g1/g1IHOPControl.hpp" -#include "gc/g1/g1Predictions.hpp" G1IHOPControl::G1IHOPControl(double initial_ihop_percent, size_t target_occupancy) : - _ihop_percent(initial_ihop_percent), + _initial_ihop_percent(initial_ihop_percent), _target_occupancy(target_occupancy) { - assert(_ihop_percent >= 0.0 && _ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); + assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); } G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, size_t target_occupancy) : @@ -52,7 +51,7 @@ ergo_format_double("recent old gen allocation rate") ergo_format_ms("recent marking phase length"), get_conc_mark_start_threshold(), - (double) get_conc_mark_start_threshold() / _target_occupancy * 100.0, + _initial_ihop_percent, _target_occupancy, G1CollectedHeap::heap()->used(), _last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0, @@ -63,7 +62,7 @@ static void test_update(G1IHOPControl* ctrl, double alloc_time, size_t alloc_amount, size_t young_size, double mark_time) { for (int i = 0; i < 100; i++) { ctrl->update_allocation_info(alloc_time, alloc_amount, young_size); - ctrl->update_time_to_mixed(mark_time); + ctrl->update_marking_length(mark_time); } } @@ -82,7 +81,7 @@ assert(threshold == initial_ihop, "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); - ctrl.update_time_to_mixed(1000.0); + ctrl.update_marking_length(1000.0); threshold = ctrl.get_conc_mark_start_threshold(); assert(threshold == initial_ihop, "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); --- old/src/share/vm/gc/g1/g1IHOPControl.hpp 2015-11-16 16:53:04.437401485 +0100 +++ new/src/share/vm/gc/g1/g1IHOPControl.hpp 2015-11-16 16:53:04.349398837 +0100 @@ -26,14 +26,15 @@ #define SHARE_VM_GC_G1_G1IHOPCONTROL_HPP #include "memory/allocation.hpp" -#include "utilities/numberSeq.hpp" -class G1Predictions; - -// Manages the decision about the threshold when concurrent marking should start. +// Base class for algorithms that calculate the heap occupancy at which +// concurrent marking should start. This heap usage threshold should be relative +// to old gen size. class G1IHOPControl : public CHeapObj { protected: - double _ihop_percent; + // The initial IHOP value relative to the target occupancy. + double _initial_ihop_percent; + // The target maximum occupancy of the heap. size_t _target_occupancy; // Initialize an instance with the initial IHOP value in percent and the target @@ -43,26 +44,39 @@ public: virtual ~G1IHOPControl() { } - // Get the current marking threshold in bytes. + // Get the current non-young occupancy at which concurrent marking should start. virtual size_t get_conc_mark_start_threshold() = 0; - // Update information about recent time during which allocations happened, - // how many allocations happened and an additional safety buffer. + // Update information about time during which allocations in the Java heap occurred, + // how large these allocations were in bytes, and an additional buffer. + // The allocations should contain any amount of space made unusable for further + // allocation, e.g. any waste caused by TLAB allocation, space at the end of + // humongous objects that can not be used for allocation, etc. + // Together with the target occupancy, this additional buffer should contain the + // difference between old gen size and total heap size at the start of reclamation, + // and space required for that reclamation. virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) = 0; - // Update the time from the end of initial mark to the first mixed gc. - virtual void update_time_to_mixed(double marking_length_s) = 0; + // Update the time spent in the mutator beginning from the end of initial mark to + // the first mixed gc. + virtual void update_marking_length(double marking_length_s) = 0; virtual void print() = 0; }; +// The returned concurrent mark starting occupancy threshold is a fixed value +// relative to the maximum heap size. class G1StaticIHOPControl : public G1IHOPControl { + // Most recent complete mutator allocation period in seconds. double _last_allocation_time_s; + // Amount of bytes allocated during _last_allocation_time_s. size_t _last_allocated_bytes; + // Most recent mutator time between the end of initial mark to the start of the + // first mixed gc. double _last_marking_length_s; public: G1StaticIHOPControl(double ihop_percent, size_t target_occupancy); - size_t get_conc_mark_start_threshold() { return (size_t) (_ihop_percent * _target_occupancy / 100.0); } + size_t get_conc_mark_start_threshold() { return (size_t) (_initial_ihop_percent * _target_occupancy / 100.0); } virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); @@ -70,7 +84,7 @@ _last_allocated_bytes = allocated_bytes; } - virtual void update_time_to_mixed(double marking_length_s) { + virtual void update_marking_length(double marking_length_s) { assert(marking_length_s > 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); _last_marking_length_s = marking_length_s; } --- /dev/null 2015-11-16 09:21:03.882507650 +0100 +++ new/src/share/vm/gc/g1/g1InitialMarkToMixedTimeTracker.hpp 2015-11-16 16:53:04.847413822 +0100 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001, 2015, 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_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP +#define SHARE_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/debug.hpp" + +// Used to track time from the end of initial mark to the first mixed GC. +// After calling the initial mark/mixed gc notifications, the result can be +// obtained in last_marking_time() once, after which the tracking resets. +// Any pauses recorded by add_pause() will be subtracted from that results. +class G1InitialMarkToMixedTimeTracker VALUE_OBJ_CLASS_SPEC { +private: + bool _active; + double _initial_mark_end_time; + double _mixed_start_time; + double _total_pause_time; + + double wall_time() const { + return _mixed_start_time - _initial_mark_end_time; + } +public: + G1InitialMarkToMixedTimeTracker() { reset(); } + + // Record initial mark pause end, starting the time tracking. + void record_initial_mark_end(double end_time) { + assert(!_active, "Initial mark out of order."); + _initial_mark_end_time = end_time; + _active = true; + } + + // Record the first mixed gc pause start, ending the time tracking. + void record_mixed_gc_start(double start_time) { + if (_active) { + _mixed_start_time = start_time; + _active = false; + } + } + + double last_marking_time() { + assert(has_result(), "Do not have all measurements yet."); + double result = (_mixed_start_time - _initial_mark_end_time) - _total_pause_time; + reset(); + return result; + } + + void reset() { + _active = false; + _total_pause_time = 0.0; + _initial_mark_end_time = -1.0; + _mixed_start_time = -1.0; + } + + void add_pause(double time) { + if (_active) { + _total_pause_time += time; + } + } + + // Returns whether we have a result that can be retrieved. + bool has_result() const { return _mixed_start_time > 0.0 && _initial_mark_end_time > 0.0; } +}; + +#endif // SHARE_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP