1 /* 2 * Copyright Amazon.com Inc. 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 #include "precompiled.hpp" 25 26 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" 27 #include "gc/shenandoah/mode/shenandoahMode.hpp" 28 #include "gc/shenandoah/shenandoahControlThread.hpp" 29 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 30 #include "gc/shenandoah/shenandoahOldGeneration.hpp" 31 #include "gc/shenandoah/shenandoahRegulatorThread.hpp" 32 #include "gc/shenandoah/shenandoahYoungGeneration.hpp" 33 #include "logging/log.hpp" 34 35 static ShenandoahHeuristics* get_heuristics(ShenandoahGeneration* nullable) { 36 return nullable != nullptr ? nullable->heuristics() : nullptr; 37 } 38 39 ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahControlThread* control_thread) : 40 ConcurrentGCThread(), 41 _control_thread(control_thread), 42 _sleep(ShenandoahControlIntervalMin), 43 _last_sleep_adjust_time(os::elapsedTime()) { 44 45 ShenandoahHeap* heap = ShenandoahHeap::heap(); 46 _old_heuristics = get_heuristics(heap->old_generation()); 47 _young_heuristics = get_heuristics(heap->young_generation()); 48 _global_heuristics = get_heuristics(heap->global_generation()); 49 50 create_and_start(); 51 } 52 53 void ShenandoahRegulatorThread::run_service() { 54 if (ShenandoahHeap::heap()->mode()->is_generational()) { 55 if (ShenandoahAllowOldMarkingPreemption) { 56 regulate_concurrent_cycles(); 57 } else { 58 regulate_interleaved_cycles(); 59 } 60 } else { 61 regulate_heap(); 62 } 63 64 log_info(gc)("%s: Done.", name()); 65 } 66 67 void ShenandoahRegulatorThread::regulate_concurrent_cycles() { 68 assert(_young_heuristics != nullptr, "Need young heuristics."); 69 assert(_old_heuristics != nullptr, "Need old heuristics."); 70 71 while (!should_terminate()) { 72 ShenandoahControlThread::GCMode mode = _control_thread->gc_mode(); 73 if (mode == ShenandoahControlThread::none) { 74 if (should_unload_classes()) { 75 if (request_concurrent_gc(ShenandoahControlThread::select_global_generation())) { 76 log_info(gc)("Heuristics request for global (unload classes) accepted."); 77 } 78 } else { 79 if (_young_heuristics->should_start_gc()) { 80 if (start_old_cycle()) { 81 log_info(gc)("Heuristics request for old collection accepted"); 82 } else if (request_concurrent_gc(YOUNG)) { 83 log_info(gc)("Heuristics request for young collection accepted"); 84 } 85 } 86 } 87 } else if (mode == ShenandoahControlThread::servicing_old) { 88 if (start_young_cycle()) { 89 log_info(gc)("Heuristics request to interrupt old for young collection accepted"); 90 } 91 } 92 93 regulator_sleep(); 94 } 95 } 96 97 void ShenandoahRegulatorThread::regulate_interleaved_cycles() { 98 assert(_young_heuristics != nullptr, "Need young heuristics."); 99 assert(_global_heuristics != nullptr, "Need global heuristics."); 100 101 while (!should_terminate()) { 102 if (_control_thread->gc_mode() == ShenandoahControlThread::none) { 103 if (start_global_cycle()) { 104 log_info(gc)("Heuristics request for global collection accepted."); 105 } else if (start_young_cycle()) { 106 log_info(gc)("Heuristics request for young collection accepted."); 107 } 108 } 109 110 regulator_sleep(); 111 } 112 } 113 114 void ShenandoahRegulatorThread::regulate_heap() { 115 assert(_global_heuristics != nullptr, "Need global heuristics."); 116 117 while (!should_terminate()) { 118 if (_control_thread->gc_mode() == ShenandoahControlThread::none) { 119 if (start_global_cycle()) { 120 log_info(gc)("Heuristics request for global collection accepted."); 121 } 122 } 123 124 regulator_sleep(); 125 } 126 } 127 128 void ShenandoahRegulatorThread::regulator_sleep() { 129 // Wait before performing the next action. If allocation happened during this wait, 130 // we exit sooner, to let heuristics re-evaluate new conditions. If we are at idle, 131 // back off exponentially. 132 double current = os::elapsedTime(); 133 134 if (_heap_changed.try_unset()) { 135 _sleep = ShenandoahControlIntervalMin; 136 } else if ((current - _last_sleep_adjust_time) * 1000 > ShenandoahControlIntervalAdjustPeriod){ 137 _sleep = MIN2<int>(ShenandoahControlIntervalMax, MAX2(1, _sleep * 2)); 138 _last_sleep_adjust_time = current; 139 } 140 141 os::naked_short_sleep(_sleep); 142 if (LogTarget(Debug, gc, thread)::is_enabled()) { 143 double elapsed = os::elapsedTime() - current; 144 double hiccup = elapsed - double(_sleep); 145 if (hiccup > 0.001) { 146 log_debug(gc, thread)("Regulator hiccup time: %.3fs", hiccup); 147 } 148 } 149 } 150 151 bool ShenandoahRegulatorThread::start_old_cycle() { 152 // TODO: These first two checks might be vestigial 153 return !ShenandoahHeap::heap()->doing_mixed_evacuations() 154 && !ShenandoahHeap::heap()->collection_set()->has_old_regions() 155 && _old_heuristics->should_start_gc() 156 && request_concurrent_gc(OLD); 157 } 158 159 bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGenerationType generation) { 160 double now = os::elapsedTime(); 161 bool accepted = _control_thread->request_concurrent_gc(generation); 162 if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) { 163 double wait_time = os::elapsedTime() - now; 164 if (wait_time > 0.001) { 165 log_debug(gc, thread)("Regulator waited %.3fs for control thread to acknowledge request.", wait_time); 166 } 167 } 168 return accepted; 169 } 170 171 bool ShenandoahRegulatorThread::start_young_cycle() { 172 return _young_heuristics->should_start_gc() && request_concurrent_gc(YOUNG); 173 } 174 175 bool ShenandoahRegulatorThread::start_global_cycle() { 176 return _global_heuristics->should_start_gc() && request_concurrent_gc(ShenandoahControlThread::select_global_generation()); 177 } 178 179 void ShenandoahRegulatorThread::stop_service() { 180 log_info(gc)("%s: Stop requested.", name()); 181 } 182 183 bool ShenandoahRegulatorThread::should_unload_classes() { 184 // The heuristics delegate this decision to the collector policy, which is based on the number 185 // of cycles started. 186 return _global_heuristics->should_unload_classes(); 187 } 188