1 /* 2 * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. 3 * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 #include "precompiled.hpp" 27 28 #include "gc/shared/satbMarkQueue.hpp" 29 #include "gc/shared/strongRootsScope.hpp" 30 #include "gc/shared/taskTerminator.hpp" 31 #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" 32 #include "gc/shenandoah/shenandoahClosures.inline.hpp" 33 #include "gc/shenandoah/shenandoahConcurrentMark.hpp" 34 #include "gc/shenandoah/shenandoahGeneration.hpp" 35 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 36 #include "gc/shenandoah/shenandoahMark.inline.hpp" 37 #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" 38 #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" 39 #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" 40 #include "gc/shenandoah/shenandoahPhaseTimings.hpp" 41 #include "gc/shenandoah/shenandoahStringDedup.hpp" 42 #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" 43 #include "gc/shenandoah/shenandoahUtils.hpp" 44 #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" 45 #include "memory/iterator.inline.hpp" 46 #include "memory/resourceArea.hpp" 47 #include "runtime/continuation.hpp" 48 #include "runtime/threads.hpp" 49 50 template <ShenandoahGenerationType GENERATION> 51 class ShenandoahConcurrentMarkingTask : public WorkerTask { 52 private: 53 ShenandoahConcurrentMark* const _cm; 54 TaskTerminator* const _terminator; 55 56 public: 57 ShenandoahConcurrentMarkingTask(ShenandoahConcurrentMark* cm, TaskTerminator* terminator) : 58 WorkerTask("Shenandoah Concurrent Mark"), _cm(cm), _terminator(terminator) { 59 } 60 61 void work(uint worker_id) { 62 ShenandoahHeap* heap = ShenandoahHeap::heap(); 63 ShenandoahParallelWorkerSession worker_session(worker_id); 64 ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_mark, ShenandoahPhaseTimings::ParallelMark, worker_id, true); 65 ShenandoahSuspendibleThreadSetJoiner stsj; 66 // Do not use active_generation() : we must use the gc_generation() set by 67 // ShenandoahGCScope on the ControllerThread's stack; no safepoint may 68 // intervene to update active_generation, so we can't 69 // shenandoah_assert_generations_reconciled() here. 70 ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); 71 assert(rp != nullptr, "need reference processor"); 72 StringDedup::Requests requests; 73 _cm->mark_loop(worker_id, _terminator, rp, GENERATION, true /*cancellable*/, 74 ShenandoahStringDedup::is_enabled() ? ENQUEUE_DEDUP : NO_DEDUP, 75 &requests); 76 } 77 }; 78 79 class ShenandoahSATBAndRemarkThreadsClosure : public ThreadClosure { 80 private: 81 SATBMarkQueueSet& _satb_qset; 82 83 public: 84 explicit ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset) : 85 _satb_qset(satb_qset) {} 86 87 void do_thread(Thread* thread) override { 88 // Transfer any partial buffer to the qset for completed buffer processing. 89 _satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread)); 90 } 91 }; 92 93 template <ShenandoahGenerationType GENERATION> 94 class ShenandoahFinalMarkingTask : public WorkerTask { 95 private: 96 ShenandoahConcurrentMark* _cm; 97 TaskTerminator* _terminator; 98 bool _dedup_string; 99 100 public: 101 ShenandoahFinalMarkingTask(ShenandoahConcurrentMark* cm, TaskTerminator* terminator, bool dedup_string) : 102 WorkerTask("Shenandoah Final Mark"), _cm(cm), _terminator(terminator), _dedup_string(dedup_string) { 103 } 104 105 void work(uint worker_id) { 106 ShenandoahHeap* heap = ShenandoahHeap::heap(); 107 108 ShenandoahParallelWorkerSession worker_session(worker_id); 109 StringDedup::Requests requests; 110 ShenandoahReferenceProcessor* rp = heap->gc_generation()->ref_processor(); 111 shenandoah_assert_generations_reconciled(); 112 113 // First drain remaining SATB buffers. 114 { 115 ShenandoahObjToScanQueue* q = _cm->get_queue(worker_id); 116 ShenandoahObjToScanQueue* old_q = _cm->get_old_queue(worker_id); 117 118 ShenandoahSATBBufferClosure<GENERATION> cl(q, old_q); 119 SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); 120 while (satb_mq_set.apply_closure_to_completed_buffer(&cl)) {} 121 assert(!heap->has_forwarded_objects(), "Not expected"); 122 123 ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set); 124 Threads::possibly_parallel_threads_do(true /* is_par */, &tc); 125 } 126 _cm->mark_loop(worker_id, _terminator, rp, GENERATION, false /*not cancellable*/, 127 _dedup_string ? ENQUEUE_DEDUP : NO_DEDUP, 128 &requests); 129 assert(_cm->task_queues()->is_empty(), "Should be empty"); 130 } 131 }; 132 133 ShenandoahConcurrentMark::ShenandoahConcurrentMark(ShenandoahGeneration* generation) : 134 ShenandoahMark(generation) {} 135 136 // Mark concurrent roots during concurrent phases 137 template <ShenandoahGenerationType GENERATION> 138 class ShenandoahMarkConcurrentRootsTask : public WorkerTask { 139 private: 140 SuspendibleThreadSetJoiner _sts_joiner; 141 ShenandoahConcurrentRootScanner _root_scanner; 142 ShenandoahObjToScanQueueSet* const _queue_set; 143 ShenandoahObjToScanQueueSet* const _old_queue_set; 144 ShenandoahReferenceProcessor* const _rp; 145 146 public: 147 ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, 148 ShenandoahObjToScanQueueSet* old, 149 ShenandoahReferenceProcessor* rp, 150 ShenandoahPhaseTimings::Phase phase, 151 uint nworkers); 152 void work(uint worker_id); 153 }; 154 155 template <ShenandoahGenerationType GENERATION> 156 ShenandoahMarkConcurrentRootsTask<GENERATION>::ShenandoahMarkConcurrentRootsTask(ShenandoahObjToScanQueueSet* qs, 157 ShenandoahObjToScanQueueSet* old, 158 ShenandoahReferenceProcessor* rp, 159 ShenandoahPhaseTimings::Phase phase, 160 uint nworkers) : 161 WorkerTask("Shenandoah Concurrent Mark Roots"), 162 _root_scanner(nworkers, phase), 163 _queue_set(qs), 164 _old_queue_set(old), 165 _rp(rp) { 166 assert(!ShenandoahHeap::heap()->has_forwarded_objects(), "Not expected"); 167 } 168 169 template <ShenandoahGenerationType GENERATION> 170 void ShenandoahMarkConcurrentRootsTask<GENERATION>::work(uint worker_id) { 171 ShenandoahConcurrentWorkerSession worker_session(worker_id); 172 ShenandoahObjToScanQueue* q = _queue_set->queue(worker_id); 173 ShenandoahObjToScanQueue* old_q = (_old_queue_set == nullptr) ? 174 nullptr : _old_queue_set->queue(worker_id); 175 ShenandoahMarkRefsClosure<GENERATION> cl(q, _rp, old_q); 176 _root_scanner.roots_do(&cl, worker_id); 177 } 178 179 void ShenandoahConcurrentMark::mark_concurrent_roots() { 180 ShenandoahHeap* const heap = ShenandoahHeap::heap(); 181 assert(!heap->has_forwarded_objects(), "Not expected"); 182 183 WorkerThreads* workers = heap->workers(); 184 ShenandoahReferenceProcessor* rp = _generation->ref_processor(); 185 _generation->reserve_task_queues(workers->active_workers()); 186 switch (_generation->type()) { 187 case YOUNG: { 188 ShenandoahMarkConcurrentRootsTask<YOUNG> task(task_queues(), old_task_queues(), rp, 189 ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); 190 workers->run_task(&task); 191 break; 192 } 193 case GLOBAL: { 194 assert(old_task_queues() == nullptr, "Global mark should not have old gen mark queues"); 195 ShenandoahMarkConcurrentRootsTask<GLOBAL> task(task_queues(), nullptr, rp, 196 ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); 197 workers->run_task(&task); 198 break; 199 } 200 case NON_GEN: { 201 assert(old_task_queues() == nullptr, "Non-generational mark should not have old gen mark queues"); 202 ShenandoahMarkConcurrentRootsTask<NON_GEN> task(task_queues(), nullptr, rp, 203 ShenandoahPhaseTimings::conc_mark_roots, workers->active_workers()); 204 workers->run_task(&task); 205 break; 206 } 207 case OLD: { 208 // We use a YOUNG generation cycle to bootstrap concurrent old marking. 209 ShouldNotReachHere(); 210 break; 211 } 212 default: 213 ShouldNotReachHere(); 214 } 215 } 216 217 class ShenandoahFlushSATBHandshakeClosure : public HandshakeClosure { 218 private: 219 SATBMarkQueueSet& _qset; 220 public: 221 ShenandoahFlushSATBHandshakeClosure(SATBMarkQueueSet& qset) : 222 HandshakeClosure("Shenandoah Flush SATB"), 223 _qset(qset) {} 224 225 void do_thread(Thread* thread) { 226 _qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread)); 227 } 228 }; 229 230 void ShenandoahConcurrentMark::concurrent_mark() { 231 ShenandoahHeap* const heap = ShenandoahHeap::heap(); 232 WorkerThreads* workers = heap->workers(); 233 uint nworkers = workers->active_workers(); 234 task_queues()->reserve(nworkers); 235 236 ShenandoahGenerationType gen_type = _generation->type(); 237 ShenandoahSATBMarkQueueSet& qset = ShenandoahBarrierSet::satb_mark_queue_set(); 238 ShenandoahFlushSATBHandshakeClosure flush_satb(qset); 239 for (uint flushes = 0; flushes < ShenandoahMaxSATBBufferFlushes; flushes++) { 240 switch (gen_type) { 241 case YOUNG: { 242 TaskTerminator terminator(nworkers, task_queues()); 243 ShenandoahConcurrentMarkingTask<YOUNG> task(this, &terminator); 244 workers->run_task(&task); 245 break; 246 } 247 case OLD: { 248 TaskTerminator terminator(nworkers, task_queues()); 249 ShenandoahConcurrentMarkingTask<OLD> task(this, &terminator); 250 workers->run_task(&task); 251 break; 252 } 253 case GLOBAL: { 254 TaskTerminator terminator(nworkers, task_queues()); 255 ShenandoahConcurrentMarkingTask<GLOBAL> task(this, &terminator); 256 workers->run_task(&task); 257 break; 258 } 259 case NON_GEN: { 260 TaskTerminator terminator(nworkers, task_queues()); 261 ShenandoahConcurrentMarkingTask<NON_GEN> task(this, &terminator); 262 workers->run_task(&task); 263 break; 264 } 265 default: 266 ShouldNotReachHere(); 267 } 268 269 if (heap->cancelled_gc()) { 270 // GC is cancelled, break out. 271 break; 272 } 273 274 size_t before = qset.completed_buffers_num(); 275 { 276 ShenandoahTimingsTracker t(ShenandoahPhaseTimings::conc_mark_satb_flush, true); 277 Handshake::execute(&flush_satb); 278 } 279 size_t after = qset.completed_buffers_num(); 280 281 if (before == after) { 282 // No more retries needed, break out. 283 break; 284 } 285 } 286 assert(task_queues()->is_empty() || heap->cancelled_gc(), "Should be empty when not cancelled"); 287 } 288 289 void ShenandoahConcurrentMark::finish_mark() { 290 assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); 291 assert(Thread::current()->is_VM_thread(), "Must by VM Thread"); 292 finish_mark_work(); 293 assert(task_queues()->is_empty(), "Should be empty"); 294 TASKQUEUE_STATS_ONLY(task_queues()->print_and_reset_taskqueue_stats("")); 295 296 _generation->set_concurrent_mark_in_progress(false); 297 _generation->set_mark_complete(); 298 299 end_mark(); 300 } 301 302 void ShenandoahConcurrentMark::finish_mark_work() { 303 // Finally mark everything else we've got in our queues during the previous steps. 304 // It does two different things for concurrent vs. mark-compact GC: 305 // - For concurrent GC, it starts with empty task queues, drains the remaining 306 // SATB buffers, and then completes the marking closure. 307 // - For mark-compact GC, it starts out with the task queues seeded by initial 308 // root scan, and completes the closure, thus marking through all live objects 309 // The implementation is the same, so it's shared here. 310 ShenandoahHeap* const heap = ShenandoahHeap::heap(); 311 ShenandoahGCPhase phase(ShenandoahPhaseTimings::finish_mark); 312 uint nworkers = heap->workers()->active_workers(); 313 task_queues()->reserve(nworkers); 314 315 StrongRootsScope scope(nworkers); 316 TaskTerminator terminator(nworkers, task_queues()); 317 318 switch (_generation->type()) { 319 case YOUNG:{ 320 ShenandoahFinalMarkingTask<YOUNG> task(this, &terminator, ShenandoahStringDedup::is_enabled()); 321 heap->workers()->run_task(&task); 322 break; 323 } 324 case OLD:{ 325 ShenandoahFinalMarkingTask<OLD> task(this, &terminator, ShenandoahStringDedup::is_enabled()); 326 heap->workers()->run_task(&task); 327 break; 328 } 329 case GLOBAL:{ 330 ShenandoahFinalMarkingTask<GLOBAL> task(this, &terminator, ShenandoahStringDedup::is_enabled()); 331 heap->workers()->run_task(&task); 332 break; 333 } 334 case NON_GEN:{ 335 ShenandoahFinalMarkingTask<NON_GEN> task(this, &terminator, ShenandoahStringDedup::is_enabled()); 336 heap->workers()->run_task(&task); 337 break; 338 } 339 default: 340 ShouldNotReachHere(); 341 } 342 343 344 assert(task_queues()->is_empty(), "Should be empty"); 345 }