1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "gc/g1/g1CollectedHeap.inline.hpp" 27 #include "gc/g1/g1IHOPControl.hpp" 28 #include "gc/g1/g1Predictions.hpp" 29 #include "gc/shared/gcTrace.hpp" 30 #include "logging/log.hpp" 31 32 G1IHOPControl::G1IHOPControl(double initial_ihop_percent, size_t target_occupancy) : 33 _initial_ihop_percent(initial_ihop_percent), 34 _target_occupancy(target_occupancy), 35 _last_allocated_bytes(0), 36 _last_allocation_time_s(0.0) 37 { 38 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); 39 } 40 41 void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { 42 assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); 43 44 _last_allocation_time_s = allocation_time_s; 45 _last_allocated_bytes = allocated_bytes; 46 } 47 48 void G1IHOPControl::print() { 49 size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); 50 log_debug(gc, ihop)("Basic information (value update), threshold: " SIZE_FORMAT "B (%1.2f), target occupancy: " SIZE_FORMAT "B, current occupancy: " SIZE_FORMAT "B," 51 " recent old gen allocation rate: %1.2f, recent marking phase length: %1.2f", 52 cur_conc_mark_start_threshold, 53 cur_conc_mark_start_threshold * 100.0 / _target_occupancy, 54 _target_occupancy, 55 G1CollectedHeap::heap()->used(), 56 _last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0, 57 last_marking_length_s()); 58 } 59 60 void G1IHOPControl::send_trace_event(G1NewTracer* tracer) { 61 tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), 62 _target_occupancy, 63 G1CollectedHeap::heap()->used(), 64 _last_allocated_bytes, 65 _last_allocation_time_s, 66 last_marking_length_s()); 67 } 68 69 G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, size_t target_occupancy) : 70 G1IHOPControl(ihop_percent, target_occupancy), 71 _last_marking_length_s(0.0) { 72 assert(_target_occupancy > 0, "Target occupancy must be larger than zero."); 73 } 74 75 #ifndef PRODUCT 76 static void test_update(G1IHOPControl* ctrl, double alloc_time, size_t alloc_amount, size_t young_size, double mark_time) { 77 for (int i = 0; i < 100; i++) { 78 ctrl->update_allocation_info(alloc_time, alloc_amount, young_size); 79 ctrl->update_marking_length(mark_time); 80 } 81 } 82 83 void G1StaticIHOPControl::test() { 84 size_t const initial_ihop = 45; 85 86 G1StaticIHOPControl ctrl(initial_ihop, 100); 87 88 size_t threshold = ctrl.get_conc_mark_start_threshold(); 89 assert(threshold == initial_ihop, 90 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 91 92 ctrl.update_allocation_info(100.0, 100, 100); 93 threshold = ctrl.get_conc_mark_start_threshold(); 94 assert(threshold == initial_ihop, 95 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 96 97 ctrl.update_marking_length(1000.0); 98 threshold = ctrl.get_conc_mark_start_threshold(); 99 assert(threshold == initial_ihop, 100 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 101 102 // Whatever we pass, the IHOP value must stay the same. 103 test_update(&ctrl, 2, 10, 10, 3); 104 threshold = ctrl.get_conc_mark_start_threshold(); 105 assert(threshold == initial_ihop, 106 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 107 108 test_update(&ctrl, 12, 10, 10, 3); 109 threshold = ctrl.get_conc_mark_start_threshold(); 110 assert(threshold == initial_ihop, 111 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 112 } 113 #endif 114 115 G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent, 116 size_t initial_target_occupancy, 117 G1Predictions const* predictor, 118 size_t heap_reserve_percent, 119 size_t heap_waste_percent) : 120 G1IHOPControl(ihop_percent, initial_target_occupancy), 121 _predictor(predictor), 122 _marking_times_s(10, 0.95), 123 _allocation_rate_s(10, 0.95), 124 _last_unrestrained_young_size(0), 125 _heap_reserve_percent(heap_reserve_percent), 126 _heap_waste_percent(heap_waste_percent) 127 { 128 } 129 130 size_t G1AdaptiveIHOPControl::actual_target_threshold() const { 131 // The actual target threshold takes the heap reserve and the expected waste in 132 // free space into account. 133 // _heap_reserve is that part of the total heap capacity that is reserved for 134 // eventual promotion failure. 135 // _heap_waste is the amount of space will never be reclaimed in any 136 // heap, so can not be used for allocation during marking and must always be 137 // considered. 138 139 double safe_total_heap_percentage = MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0); 140 141 return (size_t)MIN2( 142 G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0, 143 _target_occupancy * (100.0 - _heap_waste_percent) / 100.0 144 ); 145 } 146 147 bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const { 148 return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && 149 ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); 150 } 151 152 size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() { 153 if (have_enough_data_for_prediction()) { 154 double pred_marking_time = _predictor->get_new_prediction(&_marking_times_s); 155 double pred_promotion_rate = _predictor->get_new_prediction(&_allocation_rate_s); 156 size_t pred_promotion_size = (size_t)(pred_marking_time * pred_promotion_rate); 157 158 // In reality we would need the maximum size of the young gen during 159 // marking. This is a conservative estimate. 160 size_t predicted_needed_bytes_during_marking = 161 pred_promotion_size + _last_unrestrained_young_size; 162 163 size_t internal_threshold = actual_target_threshold(); 164 size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < internal_threshold ? 165 internal_threshold - predicted_needed_bytes_during_marking : 166 0; 167 return predicted_initiating_threshold; 168 } else { 169 // Use the initial value. 170 return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0); 171 } 172 } 173 174 void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s, 175 size_t allocated_bytes, 176 size_t additional_buffer_size) { 177 G1IHOPControl::update_allocation_info(allocation_time_s, allocated_bytes, additional_buffer_size); 178 179 double allocation_rate = (double) allocated_bytes / allocation_time_s; 180 _allocation_rate_s.add(allocation_rate); 181 182 _last_unrestrained_young_size = additional_buffer_size; 183 } 184 185 void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) { 186 assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); 187 _marking_times_s.add(marking_length_s); 188 } 189 190 void G1AdaptiveIHOPControl::print() { 191 G1IHOPControl::print(); 192 size_t actual_target = actual_target_threshold(); 193 log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: " SIZE_FORMAT "B (%1.2f), internal target occupancy: " SIZE_FORMAT "B," 194 " predicted old gen allocation rate: %1.2f, predicted marking phase length: %1.2f, prediction active: %s", 195 get_conc_mark_start_threshold(), 196 percent_of(get_conc_mark_start_threshold(), actual_target), 197 actual_target, 198 _predictor->get_new_prediction(&_allocation_rate_s), 199 _predictor->get_new_prediction(&_marking_times_s), 200 have_enough_data_for_prediction() ? "true" : "false"); 201 } 202 203 void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer) { 204 G1IHOPControl::send_trace_event(tracer); 205 tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), 206 actual_target_threshold(), 207 G1CollectedHeap::heap()->used(), 208 _last_unrestrained_young_size, 209 _predictor->get_new_prediction(&_allocation_rate_s), 210 _predictor->get_new_prediction(&_marking_times_s), 211 have_enough_data_for_prediction()); 212 } 213 214 #ifndef PRODUCT 215 void G1AdaptiveIHOPControl::test() { 216 size_t const initial_threshold = 45; 217 size_t const young_size = 10; 218 size_t const target_size = 100; 219 220 // The final IHOP value is always 221 // target_size - (young_size + alloc_amount/alloc_time * marking_time) 222 223 G1Predictions pred(0.95); 224 G1AdaptiveIHOPControl ctrl(initial_threshold, target_size, &pred, 0, 0); 225 226 // First "load". 227 size_t const alloc_time1 = 2; 228 size_t const alloc_amount1 = 10; 229 size_t const marking_time1 = 2; 230 size_t const settled_ihop1 = target_size - (young_size + alloc_amount1/alloc_time1 * marking_time1); 231 232 size_t threshold; 233 threshold = ctrl.get_conc_mark_start_threshold(); 234 assert(threshold == initial_threshold, 235 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 236 for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) { 237 ctrl.update_allocation_info(alloc_time1, alloc_amount1, young_size); 238 ctrl.update_marking_length(marking_time1); 239 // Not enough data yet. 240 threshold = ctrl.get_conc_mark_start_threshold(); 241 assert(threshold == initial_threshold, 242 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 243 } 244 245 test_update(&ctrl, alloc_time1, alloc_amount1, young_size, marking_time1); 246 247 threshold = ctrl.get_conc_mark_start_threshold(); 248 assert(threshold == settled_ihop1, 249 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 250 251 // Second "load". A bit higher allocation rate. 252 size_t const alloc_time2 = 2; 253 size_t const alloc_amount2 = 30; 254 size_t const marking_time2 = 2; 255 size_t const settled_ihop2 = target_size - (young_size + alloc_amount2/alloc_time2 * marking_time2); 256 257 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 258 259 threshold = ctrl.get_conc_mark_start_threshold(); 260 assert(threshold < settled_ihop1, 261 "Expected IHOP threshold to settle at a value lower than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 262 263 // Third "load". Very high (impossible) allocation rate. 264 size_t const alloc_time3 = 1; 265 size_t const alloc_amount3 = 50; 266 size_t const marking_time3 = 2; 267 size_t const settled_ihop3 = 0; 268 269 test_update(&ctrl, alloc_time3, alloc_amount3, young_size, marking_time3); 270 threshold = ctrl.get_conc_mark_start_threshold(); 271 272 assert(threshold == settled_ihop3, 273 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 274 275 // And back to some arbitrary value. 276 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 277 278 threshold = ctrl.get_conc_mark_start_threshold(); 279 assert(threshold > settled_ihop3, 280 "Expected IHOP threshold to settle at value larger than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 281 } 282 283 void IHOP_test() { 284 G1StaticIHOPControl::test(); 285 } 286 #endif