| 1 | /* | 
|---|
| 2 | * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. | 
|---|
| 3 | * | 
|---|
| 4 | * This code is free software; you can redistribute it and/or modify it | 
|---|
| 5 | * under the terms of the GNU General Public License version 2 only, as | 
|---|
| 6 | * published by the Free Software Foundation. | 
|---|
| 7 | * | 
|---|
| 8 | * This code is distributed in the hope that it will be useful, but WITHOUT | 
|---|
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|---|
| 10 | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|---|
| 11 | * version 2 for more details (a copy is included in the LICENSE file that | 
|---|
| 12 | * accompanied this code). | 
|---|
| 13 | * | 
|---|
| 14 | * You should have received a copy of the GNU General Public License version | 
|---|
| 15 | * 2 along with this work; if not, write to the Free Software Foundation, | 
|---|
| 16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|---|
| 17 | * | 
|---|
| 18 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|---|
| 19 | * or visit www.oracle.com if you need additional information or have any | 
|---|
| 20 | * questions. | 
|---|
| 21 | * | 
|---|
| 22 | */ | 
|---|
| 23 |  | 
|---|
| 24 | #include "precompiled.hpp" | 
|---|
| 25 |  | 
|---|
| 26 | #include "classfile/classLoaderData.hpp" | 
|---|
| 27 | #include "classfile/classLoaderDataGraph.hpp" | 
|---|
| 28 | #include "gc/shared/referenceProcessor.hpp" | 
|---|
| 29 | #include "gc/shared/referenceProcessorPhaseTimes.hpp" | 
|---|
| 30 | #include "gc/shared/workgroup.hpp" | 
|---|
| 31 | #include "gc/shared/weakProcessor.inline.hpp" | 
|---|
| 32 | #include "gc/shenandoah/shenandoahBarrierSet.hpp" | 
|---|
| 33 | #include "gc/shenandoah/shenandoahClosures.inline.hpp" | 
|---|
| 34 | #include "gc/shenandoah/shenandoahCodeRoots.hpp" | 
|---|
| 35 | #include "gc/shenandoah/shenandoahCollectionSet.hpp" | 
|---|
| 36 | #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" | 
|---|
| 37 | #include "gc/shenandoah/shenandoahFreeSet.hpp" | 
|---|
| 38 | #include "gc/shenandoah/shenandoahPhaseTimings.hpp" | 
|---|
| 39 | #include "gc/shenandoah/shenandoahHeap.inline.hpp" | 
|---|
| 40 | #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" | 
|---|
| 41 | #include "gc/shenandoah/shenandoahHeuristics.hpp" | 
|---|
| 42 | #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" | 
|---|
| 43 | #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" | 
|---|
| 44 | #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" | 
|---|
| 45 | #include "gc/shenandoah/shenandoahStringDedup.hpp" | 
|---|
| 46 | #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" | 
|---|
| 47 | #include "gc/shenandoah/shenandoahTimingTracker.hpp" | 
|---|
| 48 | #include "gc/shenandoah/shenandoahTraversalGC.hpp" | 
|---|
| 49 | #include "gc/shenandoah/shenandoahUtils.hpp" | 
|---|
| 50 | #include "gc/shenandoah/shenandoahVerifier.hpp" | 
|---|
| 51 |  | 
|---|
| 52 | #include "memory/iterator.hpp" | 
|---|
| 53 | #include "memory/metaspace.hpp" | 
|---|
| 54 | #include "memory/resourceArea.hpp" | 
|---|
| 55 | #include "memory/universe.hpp" | 
|---|
| 56 |  | 
|---|
| 57 | /** | 
|---|
| 58 | * NOTE: We are using the SATB buffer in thread.hpp and satbMarkQueue.hpp, however, it is not an SATB algorithm. | 
|---|
| 59 | * We're using the buffer as generic oop buffer to enqueue new values in concurrent oop stores, IOW, the algorithm | 
|---|
| 60 | * is incremental-update-based. | 
|---|
| 61 | * | 
|---|
| 62 | * NOTE on interaction with TAMS: we want to avoid traversing new objects for | 
|---|
| 63 | * several reasons: | 
|---|
| 64 | * - We will not reclaim them in this cycle anyway, because they are not in the | 
|---|
| 65 | *   cset | 
|---|
| 66 | * - It makes up for the bulk of work during final-pause | 
|---|
| 67 | * - It also shortens the concurrent cycle because we don't need to | 
|---|
| 68 | *   pointlessly traverse through newly allocated objects. | 
|---|
| 69 | * - As a nice side-effect, it solves the I-U termination problem (mutators | 
|---|
| 70 | *   cannot outrun the GC by allocating like crazy) | 
|---|
| 71 | * - It is an easy way to achieve MWF. What MWF does is to also enqueue the | 
|---|
| 72 | *   target object of stores if it's new. Treating new objects live implicitely | 
|---|
| 73 | *   achieves the same, but without extra barriers. I think the effect of | 
|---|
| 74 | *   shortened final-pause (mentioned above) is the main advantage of MWF. In | 
|---|
| 75 | *   particular, we will not see the head of a completely new long linked list | 
|---|
| 76 | *   in final-pause and end up traversing huge chunks of the heap there. | 
|---|
| 77 | * - We don't need to see/update the fields of new objects either, because they | 
|---|
| 78 | *   are either still null, or anything that's been stored into them has been | 
|---|
| 79 | *   evacuated+enqueued before (and will thus be treated later). | 
|---|
| 80 | * | 
|---|
| 81 | * We achieve this by setting TAMS for each region, and everything allocated | 
|---|
| 82 | * beyond TAMS will be 'implicitely marked'. | 
|---|
| 83 | * | 
|---|
| 84 | * Gotchas: | 
|---|
| 85 | * - While we want new objects to be implicitely marked, we don't want to count | 
|---|
| 86 | *   them alive. Otherwise the next cycle wouldn't pick them up and consider | 
|---|
| 87 | *   them for cset. This means that we need to protect such regions from | 
|---|
| 88 | *   getting accidentally thrashed at the end of traversal cycle. This is why I | 
|---|
| 89 | *   keep track of alloc-regions and check is_alloc_region() in the trashing | 
|---|
| 90 | *   code. | 
|---|
| 91 | * - We *need* to traverse through evacuated objects. Those objects are | 
|---|
| 92 | *   pre-existing, and any references in them point to interesting objects that | 
|---|
| 93 | *   we need to see. We also want to count them as live, because we just | 
|---|
| 94 | *   determined that they are alive :-) I achieve this by upping TAMS | 
|---|
| 95 | *   concurrently for every gclab/gc-shared alloc before publishing the | 
|---|
| 96 | *   evacuated object. This way, the GC threads will not consider such objects | 
|---|
| 97 | *   implictely marked, and traverse through them as normal. | 
|---|
| 98 | */ | 
|---|
| 99 | class ShenandoahTraversalSATBBufferClosure : public SATBBufferClosure { | 
|---|
| 100 | private: | 
|---|
| 101 | ShenandoahObjToScanQueue* _queue; | 
|---|
| 102 | ShenandoahTraversalGC* _traversal_gc; | 
|---|
| 103 | ShenandoahHeap* const _heap; | 
|---|
| 104 |  | 
|---|
| 105 | public: | 
|---|
| 106 | ShenandoahTraversalSATBBufferClosure(ShenandoahObjToScanQueue* q) : | 
|---|
| 107 | _queue(q), | 
|---|
| 108 | _heap(ShenandoahHeap::heap()) | 
|---|
| 109 | { } | 
|---|
| 110 |  | 
|---|
| 111 | void do_buffer(void** buffer, size_t size) { | 
|---|
| 112 | for (size_t i = 0; i < size; ++i) { | 
|---|
| 113 | oop* p = (oop*) &buffer[i]; | 
|---|
| 114 | oop obj = RawAccess<>::oop_load(p); | 
|---|
| 115 | shenandoah_assert_not_forwarded(p, obj); | 
|---|
| 116 | if (_heap->marking_context()->mark(obj)) { | 
|---|
| 117 | _queue->push(ShenandoahMarkTask(obj)); | 
|---|
| 118 | } | 
|---|
| 119 | } | 
|---|
| 120 | } | 
|---|
| 121 | }; | 
|---|
| 122 |  | 
|---|
| 123 | class ShenandoahTraversalSATBThreadsClosure : public ThreadClosure { | 
|---|
| 124 | private: | 
|---|
| 125 | ShenandoahTraversalSATBBufferClosure* _satb_cl; | 
|---|
| 126 |  | 
|---|
| 127 | public: | 
|---|
| 128 | ShenandoahTraversalSATBThreadsClosure(ShenandoahTraversalSATBBufferClosure* satb_cl) : | 
|---|
| 129 | _satb_cl(satb_cl) {} | 
|---|
| 130 |  | 
|---|
| 131 | void do_thread(Thread* thread) { | 
|---|
| 132 | ShenandoahThreadLocalData::satb_mark_queue(thread).apply_closure_and_empty(_satb_cl); | 
|---|
| 133 | } | 
|---|
| 134 | }; | 
|---|
| 135 |  | 
|---|
| 136 | // Like CLDToOopClosure, but clears has_modified_oops, so that we can record modified CLDs during traversal | 
|---|
| 137 | // and remark them later during final-traversal. | 
|---|
| 138 | class ShenandoahMarkCLDClosure : public CLDClosure { | 
|---|
| 139 | private: | 
|---|
| 140 | OopClosure* _cl; | 
|---|
| 141 | public: | 
|---|
| 142 | ShenandoahMarkCLDClosure(OopClosure* cl) : _cl(cl) {} | 
|---|
| 143 | void do_cld(ClassLoaderData* cld) { | 
|---|
| 144 | cld->oops_do(_cl, true, true); | 
|---|
| 145 | } | 
|---|
| 146 | }; | 
|---|
| 147 |  | 
|---|
| 148 | // Like CLDToOopClosure, but only process modified CLDs | 
|---|
| 149 | class ShenandoahRemarkCLDClosure : public CLDClosure { | 
|---|
| 150 | private: | 
|---|
| 151 | OopClosure* _cl; | 
|---|
| 152 | public: | 
|---|
| 153 | ShenandoahRemarkCLDClosure(OopClosure* cl) : _cl(cl) {} | 
|---|
| 154 | void do_cld(ClassLoaderData* cld) { | 
|---|
| 155 | if (cld->has_modified_oops()) { | 
|---|
| 156 | cld->oops_do(_cl, true, true); | 
|---|
| 157 | } | 
|---|
| 158 | } | 
|---|
| 159 | }; | 
|---|
| 160 |  | 
|---|
| 161 | class ShenandoahInitTraversalCollectionTask : public AbstractGangTask { | 
|---|
| 162 | private: | 
|---|
| 163 | ShenandoahCSetRootScanner* _rp; | 
|---|
| 164 | ShenandoahHeap* _heap; | 
|---|
| 165 | ShenandoahCsetCodeRootsIterator* _cset_coderoots; | 
|---|
| 166 | ShenandoahStringDedupRoots       _dedup_roots; | 
|---|
| 167 |  | 
|---|
| 168 | public: | 
|---|
| 169 | ShenandoahInitTraversalCollectionTask(ShenandoahCSetRootScanner* rp) : | 
|---|
| 170 | AbstractGangTask( "Shenandoah Init Traversal Collection"), | 
|---|
| 171 | _rp(rp), | 
|---|
| 172 | _heap(ShenandoahHeap::heap()) {} | 
|---|
| 173 |  | 
|---|
| 174 | void work(uint worker_id) { | 
|---|
| 175 | ShenandoahParallelWorkerSession worker_session(worker_id); | 
|---|
| 176 |  | 
|---|
| 177 | ShenandoahEvacOOMScope oom_evac_scope; | 
|---|
| 178 | ShenandoahObjToScanQueueSet* queues = _heap->traversal_gc()->task_queues(); | 
|---|
| 179 | ShenandoahObjToScanQueue* q = queues->queue(worker_id); | 
|---|
| 180 |  | 
|---|
| 181 | bool process_refs = _heap->process_references(); | 
|---|
| 182 | bool unload_classes = _heap->unload_classes(); | 
|---|
| 183 | ReferenceProcessor* rp = NULL; | 
|---|
| 184 | if (process_refs) { | 
|---|
| 185 | rp = _heap->ref_processor(); | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | // Step 1: Process ordinary GC roots. | 
|---|
| 189 | { | 
|---|
| 190 | ShenandoahTraversalClosure roots_cl(q, rp); | 
|---|
| 191 | ShenandoahMarkCLDClosure cld_cl(&roots_cl); | 
|---|
| 192 | MarkingCodeBlobClosure code_cl(&roots_cl, CodeBlobToOopClosure::FixRelocations); | 
|---|
| 193 | if (unload_classes) { | 
|---|
| 194 | _rp->roots_do(worker_id, &roots_cl, NULL, &code_cl); | 
|---|
| 195 | } else { | 
|---|
| 196 | _rp->roots_do(worker_id, &roots_cl, &cld_cl, &code_cl); | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | AlwaysTrueClosure is_alive; | 
|---|
| 200 | _dedup_roots.oops_do(&is_alive, &roots_cl, worker_id); | 
|---|
| 201 | } | 
|---|
| 202 | } | 
|---|
| 203 | }; | 
|---|
| 204 |  | 
|---|
| 205 | class ShenandoahConcurrentTraversalCollectionTask : public AbstractGangTask { | 
|---|
| 206 | private: | 
|---|
| 207 | ShenandoahTaskTerminator* _terminator; | 
|---|
| 208 | ShenandoahHeap* _heap; | 
|---|
| 209 | public: | 
|---|
| 210 | ShenandoahConcurrentTraversalCollectionTask(ShenandoahTaskTerminator* terminator) : | 
|---|
| 211 | AbstractGangTask( "Shenandoah Concurrent Traversal Collection"), | 
|---|
| 212 | _terminator(terminator), | 
|---|
| 213 | _heap(ShenandoahHeap::heap()) {} | 
|---|
| 214 |  | 
|---|
| 215 | void work(uint worker_id) { | 
|---|
| 216 | ShenandoahConcurrentWorkerSession worker_session(worker_id); | 
|---|
| 217 | ShenandoahSuspendibleThreadSetJoiner stsj(ShenandoahSuspendibleWorkers); | 
|---|
| 218 | ShenandoahEvacOOMScope oom_evac_scope; | 
|---|
| 219 | ShenandoahTraversalGC* traversal_gc = _heap->traversal_gc(); | 
|---|
| 220 |  | 
|---|
| 221 | // Drain all outstanding work in queues. | 
|---|
| 222 | traversal_gc->main_loop(worker_id, _terminator, true); | 
|---|
| 223 | } | 
|---|
| 224 | }; | 
|---|
| 225 |  | 
|---|
| 226 | class ShenandoahFinalTraversalCollectionTask : public AbstractGangTask { | 
|---|
| 227 | private: | 
|---|
| 228 | ShenandoahAllRootScanner* _rp; | 
|---|
| 229 | ShenandoahTaskTerminator* _terminator; | 
|---|
| 230 | ShenandoahHeap* _heap; | 
|---|
| 231 | public: | 
|---|
| 232 | ShenandoahFinalTraversalCollectionTask(ShenandoahAllRootScanner* rp, ShenandoahTaskTerminator* terminator) : | 
|---|
| 233 | AbstractGangTask( "Shenandoah Final Traversal Collection"), | 
|---|
| 234 | _rp(rp), | 
|---|
| 235 | _terminator(terminator), | 
|---|
| 236 | _heap(ShenandoahHeap::heap()) {} | 
|---|
| 237 |  | 
|---|
| 238 | void work(uint worker_id) { | 
|---|
| 239 | ShenandoahParallelWorkerSession worker_session(worker_id); | 
|---|
| 240 |  | 
|---|
| 241 | ShenandoahEvacOOMScope oom_evac_scope; | 
|---|
| 242 | ShenandoahTraversalGC* traversal_gc = _heap->traversal_gc(); | 
|---|
| 243 |  | 
|---|
| 244 | ShenandoahObjToScanQueueSet* queues = traversal_gc->task_queues(); | 
|---|
| 245 | ShenandoahObjToScanQueue* q = queues->queue(worker_id); | 
|---|
| 246 |  | 
|---|
| 247 | bool process_refs = _heap->process_references(); | 
|---|
| 248 | bool unload_classes = _heap->unload_classes(); | 
|---|
| 249 | ReferenceProcessor* rp = NULL; | 
|---|
| 250 | if (process_refs) { | 
|---|
| 251 | rp = _heap->ref_processor(); | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | // Step 0: Drain outstanding SATB queues. | 
|---|
| 255 | // NOTE: we piggy-back draining of remaining thread SATB buffers on the final root scan below. | 
|---|
| 256 | ShenandoahTraversalSATBBufferClosure satb_cl(q); | 
|---|
| 257 | { | 
|---|
| 258 | // Process remaining finished SATB buffers. | 
|---|
| 259 | SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); | 
|---|
| 260 | while (satb_mq_set.apply_closure_to_completed_buffer(&satb_cl)); | 
|---|
| 261 | // Process remaining threads SATB buffers below. | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | // Step 1: Process GC roots. | 
|---|
| 265 | // For oops in code roots, they are marked, evacuated, enqueued for further traversal, | 
|---|
| 266 | // and the references to the oops are updated during init pause. New nmethods are handled | 
|---|
| 267 | // in similar way during nmethod-register process. Therefore, we don't need to rescan code | 
|---|
| 268 | // roots here. | 
|---|
| 269 | if (!_heap->is_degenerated_gc_in_progress()) { | 
|---|
| 270 | ShenandoahTraversalClosure roots_cl(q, rp); | 
|---|
| 271 | ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); | 
|---|
| 272 | if (unload_classes) { | 
|---|
| 273 | ShenandoahRemarkCLDClosure (&roots_cl); | 
|---|
| 274 | _rp->strong_roots_do(worker_id, &roots_cl, &remark_cld_cl, NULL, &tc); | 
|---|
| 275 | } else { | 
|---|
| 276 | CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); | 
|---|
| 277 | _rp->roots_do(worker_id, &roots_cl, &cld_cl, NULL, &tc); | 
|---|
| 278 | } | 
|---|
| 279 | } else { | 
|---|
| 280 | ShenandoahTraversalDegenClosure roots_cl(q, rp); | 
|---|
| 281 | ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); | 
|---|
| 282 | if (unload_classes) { | 
|---|
| 283 | ShenandoahRemarkCLDClosure (&roots_cl); | 
|---|
| 284 | _rp->strong_roots_do(worker_id, &roots_cl, &remark_cld_cl, NULL, &tc); | 
|---|
| 285 | } else { | 
|---|
| 286 | CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); | 
|---|
| 287 | _rp->roots_do(worker_id, &roots_cl, &cld_cl, NULL, &tc); | 
|---|
| 288 | } | 
|---|
| 289 | } | 
|---|
| 290 |  | 
|---|
| 291 | { | 
|---|
| 292 | ShenandoahWorkerTimings *worker_times = _heap->phase_timings()->worker_times(); | 
|---|
| 293 | ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::FinishQueues, worker_id); | 
|---|
| 294 |  | 
|---|
| 295 | // Step 3: Finally drain all outstanding work in queues. | 
|---|
| 296 | traversal_gc->main_loop(worker_id, _terminator, false); | 
|---|
| 297 | } | 
|---|
| 298 |  | 
|---|
| 299 | } | 
|---|
| 300 | }; | 
|---|
| 301 |  | 
|---|
| 302 | ShenandoahTraversalGC::ShenandoahTraversalGC(ShenandoahHeap* heap, size_t num_regions) : | 
|---|
| 303 | _heap(heap), | 
|---|
| 304 | _task_queues(new ShenandoahObjToScanQueueSet(heap->max_workers())), | 
|---|
| 305 | _traversal_set(ShenandoahHeapRegionSet()) { | 
|---|
| 306 |  | 
|---|
| 307 | // Traversal does not support concurrent code root scanning | 
|---|
| 308 | FLAG_SET_DEFAULT(ShenandoahConcurrentScanCodeRoots, false); | 
|---|
| 309 |  | 
|---|
| 310 | uint num_queues = heap->max_workers(); | 
|---|
| 311 | for (uint i = 0; i < num_queues; ++i) { | 
|---|
| 312 | ShenandoahObjToScanQueue* task_queue = new ShenandoahObjToScanQueue(); | 
|---|
| 313 | task_queue->initialize(); | 
|---|
| 314 | _task_queues->register_queue(i, task_queue); | 
|---|
| 315 | } | 
|---|
| 316 | } | 
|---|
| 317 |  | 
|---|
| 318 | ShenandoahTraversalGC::~ShenandoahTraversalGC() { | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | void ShenandoahTraversalGC::prepare_regions() { | 
|---|
| 322 | size_t num_regions = _heap->num_regions(); | 
|---|
| 323 | ShenandoahMarkingContext* const ctx = _heap->marking_context(); | 
|---|
| 324 | for (size_t i = 0; i < num_regions; i++) { | 
|---|
| 325 | ShenandoahHeapRegion* region = _heap->get_region(i); | 
|---|
| 326 | if (_heap->is_bitmap_slice_committed(region)) { | 
|---|
| 327 | if (_traversal_set.is_in(i)) { | 
|---|
| 328 | ctx->capture_top_at_mark_start(region); | 
|---|
| 329 | region->clear_live_data(); | 
|---|
| 330 | assert(ctx->is_bitmap_clear_range(region->bottom(), region->end()), "bitmap for traversal regions must be cleared"); | 
|---|
| 331 | } else { | 
|---|
| 332 | // Everything outside the traversal set is always considered live. | 
|---|
| 333 | ctx->reset_top_at_mark_start(region); | 
|---|
| 334 | } | 
|---|
| 335 | } else { | 
|---|
| 336 | // FreeSet may contain uncommitted empty regions, once they are recommitted, | 
|---|
| 337 | // their TAMS may have old values, so reset them here. | 
|---|
| 338 | ctx->reset_top_at_mark_start(region); | 
|---|
| 339 | } | 
|---|
| 340 | } | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | void ShenandoahTraversalGC::prepare() { | 
|---|
| 344 | _heap->collection_set()->clear(); | 
|---|
| 345 | assert(_heap->collection_set()->count() == 0, "collection set not clear"); | 
|---|
| 346 |  | 
|---|
| 347 | { | 
|---|
| 348 | ShenandoahGCPhase phase(ShenandoahPhaseTimings::traversal_gc_make_parsable); | 
|---|
| 349 | _heap->make_parsable(true); | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | if (UseTLAB) { | 
|---|
| 353 | ShenandoahGCPhase phase(ShenandoahPhaseTimings::traversal_gc_resize_tlabs); | 
|---|
| 354 | _heap->resize_tlabs(); | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | assert(_heap->marking_context()->is_bitmap_clear(), "need clean mark bitmap"); | 
|---|
| 358 | assert(!_heap->marking_context()->is_complete(), "should not be complete"); | 
|---|
| 359 |  | 
|---|
| 360 | ShenandoahFreeSet* free_set = _heap->free_set(); | 
|---|
| 361 | ShenandoahCollectionSet* collection_set = _heap->collection_set(); | 
|---|
| 362 |  | 
|---|
| 363 | // Find collection set | 
|---|
| 364 | _heap->heuristics()->choose_collection_set(collection_set); | 
|---|
| 365 | prepare_regions(); | 
|---|
| 366 |  | 
|---|
| 367 | // Rebuild free set | 
|---|
| 368 | free_set->rebuild(); | 
|---|
| 369 |  | 
|---|
| 370 | log_info(gc, ergo)( "Collectable Garbage: "SIZE_FORMAT "M, "SIZE_FORMAT "M CSet, "SIZE_FORMAT " CSet regions", | 
|---|
| 371 | collection_set->garbage() / M, collection_set->live_data() / M, collection_set->count()); | 
|---|
| 372 | } | 
|---|
| 373 |  | 
|---|
| 374 | void ShenandoahTraversalGC::init_traversal_collection() { | 
|---|
| 375 | assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "STW traversal GC"); | 
|---|
| 376 |  | 
|---|
| 377 | if (ShenandoahVerify) { | 
|---|
| 378 | _heap->verifier()->verify_before_traversal(); | 
|---|
| 379 | } | 
|---|
| 380 |  | 
|---|
| 381 | if (VerifyBeforeGC) { | 
|---|
| 382 | Universe::verify(); | 
|---|
| 383 | } | 
|---|
| 384 |  | 
|---|
| 385 | { | 
|---|
| 386 | ShenandoahGCPhase phase_prepare(ShenandoahPhaseTimings::traversal_gc_prepare); | 
|---|
| 387 | ShenandoahHeapLocker lock(_heap->lock()); | 
|---|
| 388 | prepare(); | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | _heap->set_concurrent_traversal_in_progress(true); | 
|---|
| 392 |  | 
|---|
| 393 | bool process_refs = _heap->process_references(); | 
|---|
| 394 | if (process_refs) { | 
|---|
| 395 | ReferenceProcessor* rp = _heap->ref_processor(); | 
|---|
| 396 | rp->enable_discovery(true /*verify_no_refs*/); | 
|---|
| 397 | rp->setup_policy(_heap->soft_ref_policy()->should_clear_all_soft_refs()); | 
|---|
| 398 | } | 
|---|
| 399 |  | 
|---|
| 400 | { | 
|---|
| 401 | ShenandoahGCPhase phase_work(ShenandoahPhaseTimings::init_traversal_gc_work); | 
|---|
| 402 | assert(_task_queues->is_empty(), "queues must be empty before traversal GC"); | 
|---|
| 403 | TASKQUEUE_STATS_ONLY(_task_queues->reset_taskqueue_stats()); | 
|---|
| 404 |  | 
|---|
| 405 | #if COMPILER2_OR_JVMCI | 
|---|
| 406 | DerivedPointerTable::clear(); | 
|---|
| 407 | #endif | 
|---|
| 408 |  | 
|---|
| 409 | { | 
|---|
| 410 | uint nworkers = _heap->workers()->active_workers(); | 
|---|
| 411 | task_queues()->reserve(nworkers); | 
|---|
| 412 | ShenandoahCSetRootScanner rp(nworkers, ShenandoahPhaseTimings::init_traversal_gc_work); | 
|---|
| 413 | ShenandoahInitTraversalCollectionTask traversal_task(&rp); | 
|---|
| 414 | _heap->workers()->run_task(&traversal_task); | 
|---|
| 415 | } | 
|---|
| 416 |  | 
|---|
| 417 | #if COMPILER2_OR_JVMCI | 
|---|
| 418 | DerivedPointerTable::update_pointers(); | 
|---|
| 419 | #endif | 
|---|
| 420 | } | 
|---|
| 421 |  | 
|---|
| 422 | if (ShenandoahPacing) { | 
|---|
| 423 | _heap->pacer()->setup_for_traversal(); | 
|---|
| 424 | } | 
|---|
| 425 | } | 
|---|
| 426 |  | 
|---|
| 427 | void ShenandoahTraversalGC::main_loop(uint w, ShenandoahTaskTerminator* t, bool sts_yield) { | 
|---|
| 428 | ShenandoahObjToScanQueue* q = task_queues()->queue(w); | 
|---|
| 429 |  | 
|---|
| 430 | // Initialize live data. | 
|---|
| 431 | jushort* ld = _heap->get_liveness_cache(w); | 
|---|
| 432 |  | 
|---|
| 433 | ReferenceProcessor* rp = NULL; | 
|---|
| 434 | if (_heap->process_references()) { | 
|---|
| 435 | rp = _heap->ref_processor(); | 
|---|
| 436 | } | 
|---|
| 437 | { | 
|---|
| 438 | if (!_heap->is_degenerated_gc_in_progress()) { | 
|---|
| 439 | if (_heap->unload_classes()) { | 
|---|
| 440 | if (ShenandoahStringDedup::is_enabled()) { | 
|---|
| 441 | ShenandoahTraversalMetadataDedupClosure cl(q, rp); | 
|---|
| 442 | main_loop_work<ShenandoahTraversalMetadataDedupClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 443 | } else { | 
|---|
| 444 | ShenandoahTraversalMetadataClosure cl(q, rp); | 
|---|
| 445 | main_loop_work<ShenandoahTraversalMetadataClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 446 | } | 
|---|
| 447 | } else { | 
|---|
| 448 | if (ShenandoahStringDedup::is_enabled()) { | 
|---|
| 449 | ShenandoahTraversalDedupClosure cl(q, rp); | 
|---|
| 450 | main_loop_work<ShenandoahTraversalDedupClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 451 | } else { | 
|---|
| 452 | ShenandoahTraversalClosure cl(q, rp); | 
|---|
| 453 | main_loop_work<ShenandoahTraversalClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 454 | } | 
|---|
| 455 | } | 
|---|
| 456 | } else { | 
|---|
| 457 | if (_heap->unload_classes()) { | 
|---|
| 458 | if (ShenandoahStringDedup::is_enabled()) { | 
|---|
| 459 | ShenandoahTraversalMetadataDedupDegenClosure cl(q, rp); | 
|---|
| 460 | main_loop_work<ShenandoahTraversalMetadataDedupDegenClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 461 | } else { | 
|---|
| 462 | ShenandoahTraversalMetadataDegenClosure cl(q, rp); | 
|---|
| 463 | main_loop_work<ShenandoahTraversalMetadataDegenClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 464 | } | 
|---|
| 465 | } else { | 
|---|
| 466 | if (ShenandoahStringDedup::is_enabled()) { | 
|---|
| 467 | ShenandoahTraversalDedupDegenClosure cl(q, rp); | 
|---|
| 468 | main_loop_work<ShenandoahTraversalDedupDegenClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 469 | } else { | 
|---|
| 470 | ShenandoahTraversalDegenClosure cl(q, rp); | 
|---|
| 471 | main_loop_work<ShenandoahTraversalDegenClosure>(&cl, ld, w, t, sts_yield); | 
|---|
| 472 | } | 
|---|
| 473 | } | 
|---|
| 474 | } | 
|---|
| 475 | } | 
|---|
| 476 |  | 
|---|
| 477 | _heap->flush_liveness_cache(w); | 
|---|
| 478 | } | 
|---|
| 479 |  | 
|---|
| 480 | template <class T> | 
|---|
| 481 | void ShenandoahTraversalGC::main_loop_work(T* cl, jushort* live_data, uint worker_id, ShenandoahTaskTerminator* terminator, bool sts_yield) { | 
|---|
| 482 | ShenandoahObjToScanQueueSet* queues = task_queues(); | 
|---|
| 483 | ShenandoahObjToScanQueue* q = queues->queue(worker_id); | 
|---|
| 484 | ShenandoahConcurrentMark* conc_mark = _heap->concurrent_mark(); | 
|---|
| 485 |  | 
|---|
| 486 | uintx stride = ShenandoahMarkLoopStride; | 
|---|
| 487 |  | 
|---|
| 488 | ShenandoahMarkTask task; | 
|---|
| 489 |  | 
|---|
| 490 | // Process outstanding queues, if any. | 
|---|
| 491 | q = queues->claim_next(); | 
|---|
| 492 | while (q != NULL) { | 
|---|
| 493 | if (_heap->check_cancelled_gc_and_yield(sts_yield)) { | 
|---|
| 494 | return; | 
|---|
| 495 | } | 
|---|
| 496 |  | 
|---|
| 497 | for (uint i = 0; i < stride; i++) { | 
|---|
| 498 | if (q->pop(task)) { | 
|---|
| 499 | conc_mark->do_task<T>(q, cl, live_data, &task); | 
|---|
| 500 | } else { | 
|---|
| 501 | assert(q->is_empty(), "Must be empty"); | 
|---|
| 502 | q = queues->claim_next(); | 
|---|
| 503 | break; | 
|---|
| 504 | } | 
|---|
| 505 | } | 
|---|
| 506 | } | 
|---|
| 507 |  | 
|---|
| 508 | if (check_and_handle_cancelled_gc(terminator, sts_yield)) return; | 
|---|
| 509 |  | 
|---|
| 510 | // Normal loop. | 
|---|
| 511 | q = queues->queue(worker_id); | 
|---|
| 512 |  | 
|---|
| 513 | ShenandoahTraversalSATBBufferClosure drain_satb(q); | 
|---|
| 514 | SATBMarkQueueSet& satb_mq_set = ShenandoahBarrierSet::satb_mark_queue_set(); | 
|---|
| 515 |  | 
|---|
| 516 | while (true) { | 
|---|
| 517 | if (check_and_handle_cancelled_gc(terminator, sts_yield)) return; | 
|---|
| 518 |  | 
|---|
| 519 | while (satb_mq_set.completed_buffers_num() > 0) { | 
|---|
| 520 | satb_mq_set.apply_closure_to_completed_buffer(&drain_satb); | 
|---|
| 521 | } | 
|---|
| 522 |  | 
|---|
| 523 | uint work = 0; | 
|---|
| 524 | for (uint i = 0; i < stride; i++) { | 
|---|
| 525 | if (q->pop(task) || | 
|---|
| 526 | queues->steal(worker_id, task)) { | 
|---|
| 527 | conc_mark->do_task<T>(q, cl, live_data, &task); | 
|---|
| 528 | work++; | 
|---|
| 529 | } else { | 
|---|
| 530 | break; | 
|---|
| 531 | } | 
|---|
| 532 | } | 
|---|
| 533 |  | 
|---|
| 534 | if (work == 0) { | 
|---|
| 535 | // No more work, try to terminate | 
|---|
| 536 | ShenandoahEvacOOMScopeLeaver oom_scope_leaver; | 
|---|
| 537 | ShenandoahSuspendibleThreadSetLeaver stsl(sts_yield && ShenandoahSuspendibleWorkers); | 
|---|
| 538 | ShenandoahTerminationTimingsTracker term_tracker(worker_id); | 
|---|
| 539 | ShenandoahTerminatorTerminator tt(_heap); | 
|---|
| 540 |  | 
|---|
| 541 | if (terminator->offer_termination(&tt)) return; | 
|---|
| 542 | } | 
|---|
| 543 | } | 
|---|
| 544 | } | 
|---|
| 545 |  | 
|---|
| 546 | bool ShenandoahTraversalGC::check_and_handle_cancelled_gc(ShenandoahTaskTerminator* terminator, bool sts_yield) { | 
|---|
| 547 | if (_heap->cancelled_gc()) { | 
|---|
| 548 | return true; | 
|---|
| 549 | } | 
|---|
| 550 | return false; | 
|---|
| 551 | } | 
|---|
| 552 |  | 
|---|
| 553 | void ShenandoahTraversalGC::concurrent_traversal_collection() { | 
|---|
| 554 | ShenandoahGCPhase phase_work(ShenandoahPhaseTimings::conc_traversal); | 
|---|
| 555 | if (!_heap->cancelled_gc()) { | 
|---|
| 556 | uint nworkers = _heap->workers()->active_workers(); | 
|---|
| 557 | task_queues()->reserve(nworkers); | 
|---|
| 558 | ShenandoahTerminationTracker tracker(ShenandoahPhaseTimings::conc_traversal_termination); | 
|---|
| 559 |  | 
|---|
| 560 | ShenandoahTaskTerminator terminator(nworkers, task_queues()); | 
|---|
| 561 | ShenandoahConcurrentTraversalCollectionTask task(&terminator); | 
|---|
| 562 | _heap->workers()->run_task(&task); | 
|---|
| 563 | } | 
|---|
| 564 |  | 
|---|
| 565 | if (!_heap->cancelled_gc() && ShenandoahPreclean && _heap->process_references()) { | 
|---|
| 566 | preclean_weak_refs(); | 
|---|
| 567 | } | 
|---|
| 568 | } | 
|---|
| 569 |  | 
|---|
| 570 | void ShenandoahTraversalGC::final_traversal_collection() { | 
|---|
| 571 | _heap->make_parsable(true); | 
|---|
| 572 |  | 
|---|
| 573 | if (!_heap->cancelled_gc()) { | 
|---|
| 574 | #if COMPILER2_OR_JVMCI | 
|---|
| 575 | DerivedPointerTable::clear(); | 
|---|
| 576 | #endif | 
|---|
| 577 | ShenandoahGCPhase phase_work(ShenandoahPhaseTimings::final_traversal_gc_work); | 
|---|
| 578 | uint nworkers = _heap->workers()->active_workers(); | 
|---|
| 579 | task_queues()->reserve(nworkers); | 
|---|
| 580 |  | 
|---|
| 581 | // Finish traversal | 
|---|
| 582 | ShenandoahAllRootScanner rp(nworkers, ShenandoahPhaseTimings::final_traversal_gc_work); | 
|---|
| 583 | ShenandoahTerminationTracker term(ShenandoahPhaseTimings::final_traversal_gc_termination); | 
|---|
| 584 |  | 
|---|
| 585 | ShenandoahTaskTerminator terminator(nworkers, task_queues()); | 
|---|
| 586 | ShenandoahFinalTraversalCollectionTask task(&rp, &terminator); | 
|---|
| 587 | _heap->workers()->run_task(&task); | 
|---|
| 588 | #if COMPILER2_OR_JVMCI | 
|---|
| 589 | DerivedPointerTable::update_pointers(); | 
|---|
| 590 | #endif | 
|---|
| 591 | } | 
|---|
| 592 |  | 
|---|
| 593 | if (!_heap->cancelled_gc() && _heap->process_references()) { | 
|---|
| 594 | weak_refs_work(); | 
|---|
| 595 | } | 
|---|
| 596 |  | 
|---|
| 597 | if (!_heap->cancelled_gc()) { | 
|---|
| 598 | fixup_roots(); | 
|---|
| 599 | if (_heap->unload_classes()) { | 
|---|
| 600 | _heap->unload_classes_and_cleanup_tables(false); | 
|---|
| 601 | } | 
|---|
| 602 | } | 
|---|
| 603 |  | 
|---|
| 604 | if (!_heap->cancelled_gc()) { | 
|---|
| 605 | assert(_task_queues->is_empty(), "queues must be empty after traversal GC"); | 
|---|
| 606 | TASKQUEUE_STATS_ONLY(_task_queues->print_taskqueue_stats()); | 
|---|
| 607 | TASKQUEUE_STATS_ONLY(_task_queues->reset_taskqueue_stats()); | 
|---|
| 608 |  | 
|---|
| 609 | // No more marking expected | 
|---|
| 610 | _heap->mark_complete_marking_context(); | 
|---|
| 611 |  | 
|---|
| 612 | // Resize metaspace | 
|---|
| 613 | MetaspaceGC::compute_new_size(); | 
|---|
| 614 |  | 
|---|
| 615 | // Still good? We can now trash the cset, and make final verification | 
|---|
| 616 | { | 
|---|
| 617 | ShenandoahGCPhase phase_cleanup(ShenandoahPhaseTimings::traversal_gc_cleanup); | 
|---|
| 618 | ShenandoahHeapLocker lock(_heap->lock()); | 
|---|
| 619 |  | 
|---|
| 620 | // Trash everything | 
|---|
| 621 | // Clear immediate garbage regions. | 
|---|
| 622 | size_t num_regions = _heap->num_regions(); | 
|---|
| 623 |  | 
|---|
| 624 | ShenandoahHeapRegionSet* traversal_regions = traversal_set(); | 
|---|
| 625 | ShenandoahFreeSet* free_regions = _heap->free_set(); | 
|---|
| 626 | ShenandoahMarkingContext* const ctx = _heap->marking_context(); | 
|---|
| 627 | free_regions->clear(); | 
|---|
| 628 | for (size_t i = 0; i < num_regions; i++) { | 
|---|
| 629 | ShenandoahHeapRegion* r = _heap->get_region(i); | 
|---|
| 630 | bool not_allocated = ctx->top_at_mark_start(r) == r->top(); | 
|---|
| 631 |  | 
|---|
| 632 | bool candidate = traversal_regions->is_in(r) && !r->has_live() && not_allocated; | 
|---|
| 633 | if (r->is_humongous_start() && candidate) { | 
|---|
| 634 | // Trash humongous. | 
|---|
| 635 | HeapWord* humongous_obj = r->bottom(); | 
|---|
| 636 | assert(!ctx->is_marked(oop(humongous_obj)), "must not be marked"); | 
|---|
| 637 | r->make_trash_immediate(); | 
|---|
| 638 | while (i + 1 < num_regions && _heap->get_region(i + 1)->is_humongous_continuation()) { | 
|---|
| 639 | i++; | 
|---|
| 640 | r = _heap->get_region(i); | 
|---|
| 641 | assert(r->is_humongous_continuation(), "must be humongous continuation"); | 
|---|
| 642 | r->make_trash_immediate(); | 
|---|
| 643 | } | 
|---|
| 644 | } else if (!r->is_empty() && candidate) { | 
|---|
| 645 | // Trash regular. | 
|---|
| 646 | assert(!r->is_humongous(), "handled above"); | 
|---|
| 647 | assert(!r->is_trash(), "must not already be trashed"); | 
|---|
| 648 | r->make_trash_immediate(); | 
|---|
| 649 | } | 
|---|
| 650 | } | 
|---|
| 651 | _heap->collection_set()->clear(); | 
|---|
| 652 | _heap->free_set()->rebuild(); | 
|---|
| 653 | reset(); | 
|---|
| 654 | } | 
|---|
| 655 |  | 
|---|
| 656 | assert(_task_queues->is_empty(), "queues must be empty after traversal GC"); | 
|---|
| 657 | _heap->set_concurrent_traversal_in_progress(false); | 
|---|
| 658 | assert(!_heap->cancelled_gc(), "must not be cancelled when getting out here"); | 
|---|
| 659 |  | 
|---|
| 660 | if (ShenandoahVerify) { | 
|---|
| 661 | _heap->verifier()->verify_after_traversal(); | 
|---|
| 662 | } | 
|---|
| 663 |  | 
|---|
| 664 | if (VerifyAfterGC) { | 
|---|
| 665 | Universe::verify(); | 
|---|
| 666 | } | 
|---|
| 667 | } | 
|---|
| 668 | } | 
|---|
| 669 |  | 
|---|
| 670 | class ShenandoahTraversalFixRootsClosure : public OopClosure { | 
|---|
| 671 | private: | 
|---|
| 672 | template <class T> | 
|---|
| 673 | inline void do_oop_work(T* p) { | 
|---|
| 674 | T o = RawAccess<>::oop_load(p); | 
|---|
| 675 | if (!CompressedOops::is_null(o)) { | 
|---|
| 676 | oop obj = CompressedOops::decode_not_null(o); | 
|---|
| 677 | oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); | 
|---|
| 678 | if (!oopDesc::equals_raw(obj, forw)) { | 
|---|
| 679 | RawAccess<IS_NOT_NULL>::oop_store(p, forw); | 
|---|
| 680 | } | 
|---|
| 681 | } | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | public: | 
|---|
| 685 | inline void do_oop(oop* p) { do_oop_work(p); } | 
|---|
| 686 | inline void do_oop(narrowOop* p) { do_oop_work(p); } | 
|---|
| 687 | }; | 
|---|
| 688 |  | 
|---|
| 689 | class ShenandoahTraversalFixRootsTask : public AbstractGangTask { | 
|---|
| 690 | private: | 
|---|
| 691 | ShenandoahRootUpdater* _rp; | 
|---|
| 692 |  | 
|---|
| 693 | public: | 
|---|
| 694 | ShenandoahTraversalFixRootsTask(ShenandoahRootUpdater* rp) : | 
|---|
| 695 | AbstractGangTask( "Shenandoah traversal fix roots"), | 
|---|
| 696 | _rp(rp) { | 
|---|
| 697 | assert(ShenandoahHeap::heap()->has_forwarded_objects(), "Must be"); | 
|---|
| 698 | } | 
|---|
| 699 |  | 
|---|
| 700 | void work(uint worker_id) { | 
|---|
| 701 | ShenandoahParallelWorkerSession worker_session(worker_id); | 
|---|
| 702 | ShenandoahTraversalFixRootsClosure cl; | 
|---|
| 703 | ShenandoahForwardedIsAliveClosure is_alive; | 
|---|
| 704 | _rp->roots_do<ShenandoahForwardedIsAliveClosure, ShenandoahTraversalFixRootsClosure>(worker_id, &is_alive, &cl); | 
|---|
| 705 | } | 
|---|
| 706 | }; | 
|---|
| 707 |  | 
|---|
| 708 | void ShenandoahTraversalGC::fixup_roots() { | 
|---|
| 709 | #if COMPILER2_OR_JVMCI | 
|---|
| 710 | DerivedPointerTable::clear(); | 
|---|
| 711 | #endif | 
|---|
| 712 | ShenandoahRootUpdater rp(_heap->workers()->active_workers(), ShenandoahPhaseTimings::final_traversal_update_roots, true /* update code cache */); | 
|---|
| 713 | ShenandoahTraversalFixRootsTask update_roots_task(&rp); | 
|---|
| 714 | _heap->workers()->run_task(&update_roots_task); | 
|---|
| 715 | #if COMPILER2_OR_JVMCI | 
|---|
| 716 | DerivedPointerTable::update_pointers(); | 
|---|
| 717 | #endif | 
|---|
| 718 | } | 
|---|
| 719 |  | 
|---|
| 720 | void ShenandoahTraversalGC::reset() { | 
|---|
| 721 | _task_queues->clear(); | 
|---|
| 722 | } | 
|---|
| 723 |  | 
|---|
| 724 | ShenandoahObjToScanQueueSet* ShenandoahTraversalGC::task_queues() { | 
|---|
| 725 | return _task_queues; | 
|---|
| 726 | } | 
|---|
| 727 |  | 
|---|
| 728 | class ShenandoahTraversalCancelledGCYieldClosure : public YieldClosure { | 
|---|
| 729 | private: | 
|---|
| 730 | ShenandoahHeap* const _heap; | 
|---|
| 731 | public: | 
|---|
| 732 | ShenandoahTraversalCancelledGCYieldClosure() : _heap(ShenandoahHeap::heap()) {}; | 
|---|
| 733 | virtual bool should_return() { return _heap->cancelled_gc(); } | 
|---|
| 734 | }; | 
|---|
| 735 |  | 
|---|
| 736 | class ShenandoahTraversalPrecleanCompleteGCClosure : public VoidClosure { | 
|---|
| 737 | public: | 
|---|
| 738 | void do_void() { | 
|---|
| 739 | ShenandoahHeap* sh = ShenandoahHeap::heap(); | 
|---|
| 740 | ShenandoahTraversalGC* traversal_gc = sh->traversal_gc(); | 
|---|
| 741 | assert(sh->process_references(), "why else would we be here?"); | 
|---|
| 742 | ShenandoahTaskTerminator terminator(1, traversal_gc->task_queues()); | 
|---|
| 743 | shenandoah_assert_rp_isalive_installed(); | 
|---|
| 744 | traversal_gc->main_loop((uint) 0, &terminator, true); | 
|---|
| 745 | } | 
|---|
| 746 | }; | 
|---|
| 747 |  | 
|---|
| 748 | class ShenandoahTraversalKeepAliveUpdateClosure : public OopClosure { | 
|---|
| 749 | private: | 
|---|
| 750 | ShenandoahObjToScanQueue* _queue; | 
|---|
| 751 | Thread* _thread; | 
|---|
| 752 | ShenandoahTraversalGC* _traversal_gc; | 
|---|
| 753 | ShenandoahMarkingContext* const _mark_context; | 
|---|
| 754 |  | 
|---|
| 755 | template <class T> | 
|---|
| 756 | inline void do_oop_work(T* p) { | 
|---|
| 757 | _traversal_gc->process_oop<T, false /* string dedup */, false /* degen */>(p, _thread, _queue, _mark_context); | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | public: | 
|---|
| 761 | ShenandoahTraversalKeepAliveUpdateClosure(ShenandoahObjToScanQueue* q) : | 
|---|
| 762 | _queue(q), _thread(Thread::current()), | 
|---|
| 763 | _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), | 
|---|
| 764 | _mark_context(ShenandoahHeap::heap()->marking_context()) {} | 
|---|
| 765 |  | 
|---|
| 766 | void do_oop(narrowOop* p) { do_oop_work(p); } | 
|---|
| 767 | void do_oop(oop* p)       { do_oop_work(p); } | 
|---|
| 768 | }; | 
|---|
| 769 |  | 
|---|
| 770 | class ShenandoahTraversalKeepAliveUpdateDegenClosure : public OopClosure { | 
|---|
| 771 | private: | 
|---|
| 772 | ShenandoahObjToScanQueue* _queue; | 
|---|
| 773 | Thread* _thread; | 
|---|
| 774 | ShenandoahTraversalGC* _traversal_gc; | 
|---|
| 775 | ShenandoahMarkingContext* const _mark_context; | 
|---|
| 776 |  | 
|---|
| 777 | template <class T> | 
|---|
| 778 | inline void do_oop_work(T* p) { | 
|---|
| 779 | _traversal_gc->process_oop<T, false /* string dedup */, true /* degen */>(p, _thread, _queue, _mark_context); | 
|---|
| 780 | } | 
|---|
| 781 |  | 
|---|
| 782 | public: | 
|---|
| 783 | ShenandoahTraversalKeepAliveUpdateDegenClosure(ShenandoahObjToScanQueue* q) : | 
|---|
| 784 | _queue(q), _thread(Thread::current()), | 
|---|
| 785 | _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), | 
|---|
| 786 | _mark_context(ShenandoahHeap::heap()->marking_context()) {} | 
|---|
| 787 |  | 
|---|
| 788 | void do_oop(narrowOop* p) { do_oop_work(p); } | 
|---|
| 789 | void do_oop(oop* p)       { do_oop_work(p); } | 
|---|
| 790 | }; | 
|---|
| 791 |  | 
|---|
| 792 | class ShenandoahTraversalSingleThreadKeepAliveUpdateClosure : public OopClosure { | 
|---|
| 793 | private: | 
|---|
| 794 | ShenandoahObjToScanQueue* _queue; | 
|---|
| 795 | Thread* _thread; | 
|---|
| 796 | ShenandoahTraversalGC* _traversal_gc; | 
|---|
| 797 | ShenandoahMarkingContext* const _mark_context; | 
|---|
| 798 |  | 
|---|
| 799 | template <class T> | 
|---|
| 800 | inline void do_oop_work(T* p) { | 
|---|
| 801 | ShenandoahEvacOOMScope evac_scope; | 
|---|
| 802 | _traversal_gc->process_oop<T, false /* string dedup */, false /* degen */>(p, _thread, _queue, _mark_context); | 
|---|
| 803 | } | 
|---|
| 804 |  | 
|---|
| 805 | public: | 
|---|
| 806 | ShenandoahTraversalSingleThreadKeepAliveUpdateClosure(ShenandoahObjToScanQueue* q) : | 
|---|
| 807 | _queue(q), _thread(Thread::current()), | 
|---|
| 808 | _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), | 
|---|
| 809 | _mark_context(ShenandoahHeap::heap()->marking_context()) {} | 
|---|
| 810 |  | 
|---|
| 811 | void do_oop(narrowOop* p) { do_oop_work(p); } | 
|---|
| 812 | void do_oop(oop* p)       { do_oop_work(p); } | 
|---|
| 813 | }; | 
|---|
| 814 |  | 
|---|
| 815 | class ShenandoahTraversalSingleThreadKeepAliveUpdateDegenClosure : public OopClosure { | 
|---|
| 816 | private: | 
|---|
| 817 | ShenandoahObjToScanQueue* _queue; | 
|---|
| 818 | Thread* _thread; | 
|---|
| 819 | ShenandoahTraversalGC* _traversal_gc; | 
|---|
| 820 | ShenandoahMarkingContext* const _mark_context; | 
|---|
| 821 |  | 
|---|
| 822 | template <class T> | 
|---|
| 823 | inline void do_oop_work(T* p) { | 
|---|
| 824 | ShenandoahEvacOOMScope evac_scope; | 
|---|
| 825 | _traversal_gc->process_oop<T, false /* string dedup */, true /* degen */>(p, _thread, _queue, _mark_context); | 
|---|
| 826 | } | 
|---|
| 827 |  | 
|---|
| 828 | public: | 
|---|
| 829 | ShenandoahTraversalSingleThreadKeepAliveUpdateDegenClosure(ShenandoahObjToScanQueue* q) : | 
|---|
| 830 | _queue(q), _thread(Thread::current()), | 
|---|
| 831 | _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), | 
|---|
| 832 | _mark_context(ShenandoahHeap::heap()->marking_context()) {} | 
|---|
| 833 |  | 
|---|
| 834 | void do_oop(narrowOop* p) { do_oop_work(p); } | 
|---|
| 835 | void do_oop(oop* p)       { do_oop_work(p); } | 
|---|
| 836 | }; | 
|---|
| 837 |  | 
|---|
| 838 | class ShenandoahTraversalPrecleanTask : public AbstractGangTask { | 
|---|
| 839 | private: | 
|---|
| 840 | ReferenceProcessor* _rp; | 
|---|
| 841 |  | 
|---|
| 842 | public: | 
|---|
| 843 | ShenandoahTraversalPrecleanTask(ReferenceProcessor* rp) : | 
|---|
| 844 | AbstractGangTask( "Precleaning task"), | 
|---|
| 845 | _rp(rp) {} | 
|---|
| 846 |  | 
|---|
| 847 | void work(uint worker_id) { | 
|---|
| 848 | assert(worker_id == 0, "The code below is single-threaded, only one worker is expected"); | 
|---|
| 849 | ShenandoahParallelWorkerSession worker_session(worker_id); | 
|---|
| 850 | ShenandoahSuspendibleThreadSetJoiner stsj(ShenandoahSuspendibleWorkers); | 
|---|
| 851 | ShenandoahEvacOOMScope oom_evac_scope; | 
|---|
| 852 |  | 
|---|
| 853 | ShenandoahHeap* sh = ShenandoahHeap::heap(); | 
|---|
| 854 |  | 
|---|
| 855 | ShenandoahObjToScanQueue* q = sh->traversal_gc()->task_queues()->queue(worker_id); | 
|---|
| 856 |  | 
|---|
| 857 | ShenandoahForwardedIsAliveClosure is_alive; | 
|---|
| 858 | ShenandoahTraversalCancelledGCYieldClosure yield; | 
|---|
| 859 | ShenandoahTraversalPrecleanCompleteGCClosure complete_gc; | 
|---|
| 860 | ShenandoahTraversalKeepAliveUpdateClosure keep_alive(q); | 
|---|
| 861 | ResourceMark rm; | 
|---|
| 862 | _rp->preclean_discovered_references(&is_alive, &keep_alive, | 
|---|
| 863 | &complete_gc, &yield, | 
|---|
| 864 | NULL); | 
|---|
| 865 | } | 
|---|
| 866 | }; | 
|---|
| 867 |  | 
|---|
| 868 | void ShenandoahTraversalGC::preclean_weak_refs() { | 
|---|
| 869 | // Pre-cleaning weak references before diving into STW makes sense at the | 
|---|
| 870 | // end of concurrent mark. This will filter out the references which referents | 
|---|
| 871 | // are alive. Note that ReferenceProcessor already filters out these on reference | 
|---|
| 872 | // discovery, and the bulk of work is done here. This phase processes leftovers | 
|---|
| 873 | // that missed the initial filtering, i.e. when referent was marked alive after | 
|---|
| 874 | // reference was discovered by RP. | 
|---|
| 875 |  | 
|---|
| 876 | assert(_heap->process_references(), "sanity"); | 
|---|
| 877 | assert(!_heap->is_degenerated_gc_in_progress(), "must be in concurrent non-degenerated phase"); | 
|---|
| 878 |  | 
|---|
| 879 | // Shortcut if no references were discovered to avoid winding up threads. | 
|---|
| 880 | ReferenceProcessor* rp = _heap->ref_processor(); | 
|---|
| 881 | if (!rp->has_discovered_references()) { | 
|---|
| 882 | return; | 
|---|
| 883 | } | 
|---|
| 884 |  | 
|---|
| 885 | ReferenceProcessorMTDiscoveryMutator fix_mt_discovery(rp, false); | 
|---|
| 886 |  | 
|---|
| 887 | shenandoah_assert_rp_isalive_not_installed(); | 
|---|
| 888 | ShenandoahForwardedIsAliveClosure is_alive; | 
|---|
| 889 | ReferenceProcessorIsAliveMutator fix_isalive(rp, &is_alive); | 
|---|
| 890 |  | 
|---|
| 891 | assert(task_queues()->is_empty(), "Should be empty"); | 
|---|
| 892 |  | 
|---|
| 893 | // Execute precleaning in the worker thread: it will give us GCLABs, String dedup | 
|---|
| 894 | // queues and other goodies. When upstream ReferenceProcessor starts supporting | 
|---|
| 895 | // parallel precleans, we can extend this to more threads. | 
|---|
| 896 | ShenandoahPushWorkerScope scope(_heap->workers(), 1, /* check_workers = */ false); | 
|---|
| 897 |  | 
|---|
| 898 | WorkGang* workers = _heap->workers(); | 
|---|
| 899 | uint nworkers = workers->active_workers(); | 
|---|
| 900 | assert(nworkers == 1, "This code uses only a single worker"); | 
|---|
| 901 | task_queues()->reserve(nworkers); | 
|---|
| 902 |  | 
|---|
| 903 | ShenandoahTraversalPrecleanTask task(rp); | 
|---|
| 904 | workers->run_task(&task); | 
|---|
| 905 |  | 
|---|
| 906 | assert(_heap->cancelled_gc() || task_queues()->is_empty(), "Should be empty"); | 
|---|
| 907 | } | 
|---|
| 908 |  | 
|---|
| 909 | // Weak Reference Closures | 
|---|
| 910 | class ShenandoahTraversalDrainMarkingStackClosure: public VoidClosure { | 
|---|
| 911 | uint _worker_id; | 
|---|
| 912 | ShenandoahTaskTerminator* _terminator; | 
|---|
| 913 | bool _reset_terminator; | 
|---|
| 914 |  | 
|---|
| 915 | public: | 
|---|
| 916 | ShenandoahTraversalDrainMarkingStackClosure(uint worker_id, ShenandoahTaskTerminator* t, bool reset_terminator = false): | 
|---|
| 917 | _worker_id(worker_id), | 
|---|
| 918 | _terminator(t), | 
|---|
| 919 | _reset_terminator(reset_terminator) { | 
|---|
| 920 | } | 
|---|
| 921 |  | 
|---|
| 922 | void do_void() { | 
|---|
| 923 | assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); | 
|---|
| 924 |  | 
|---|
| 925 | ShenandoahHeap* sh = ShenandoahHeap::heap(); | 
|---|
| 926 | ShenandoahTraversalGC* traversal_gc = sh->traversal_gc(); | 
|---|
| 927 | assert(sh->process_references(), "why else would we be here?"); | 
|---|
| 928 | shenandoah_assert_rp_isalive_installed(); | 
|---|
| 929 |  | 
|---|
| 930 | traversal_gc->main_loop(_worker_id, _terminator, false); | 
|---|
| 931 |  | 
|---|
| 932 | if (_reset_terminator) { | 
|---|
| 933 | _terminator->reset_for_reuse(); | 
|---|
| 934 | } | 
|---|
| 935 | } | 
|---|
| 936 | }; | 
|---|
| 937 |  | 
|---|
| 938 | class ShenandoahTraversalSingleThreadedDrainMarkingStackClosure: public VoidClosure { | 
|---|
| 939 | uint _worker_id; | 
|---|
| 940 | ShenandoahTaskTerminator* _terminator; | 
|---|
| 941 | bool _reset_terminator; | 
|---|
| 942 |  | 
|---|
| 943 | public: | 
|---|
| 944 | ShenandoahTraversalSingleThreadedDrainMarkingStackClosure(uint worker_id, ShenandoahTaskTerminator* t, bool reset_terminator = false): | 
|---|
| 945 | _worker_id(worker_id), | 
|---|
| 946 | _terminator(t), | 
|---|
| 947 | _reset_terminator(reset_terminator) { | 
|---|
| 948 | } | 
|---|
| 949 |  | 
|---|
| 950 | void do_void() { | 
|---|
| 951 | assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); | 
|---|
| 952 |  | 
|---|
| 953 | ShenandoahHeap* sh = ShenandoahHeap::heap(); | 
|---|
| 954 | ShenandoahTraversalGC* traversal_gc = sh->traversal_gc(); | 
|---|
| 955 | assert(sh->process_references(), "why else would we be here?"); | 
|---|
| 956 | shenandoah_assert_rp_isalive_installed(); | 
|---|
| 957 |  | 
|---|
| 958 | ShenandoahEvacOOMScope evac_scope; | 
|---|
| 959 | traversal_gc->main_loop(_worker_id, _terminator, false); | 
|---|
| 960 |  | 
|---|
| 961 | if (_reset_terminator) { | 
|---|
| 962 | _terminator->reset_for_reuse(); | 
|---|
| 963 | } | 
|---|
| 964 | } | 
|---|
| 965 | }; | 
|---|
| 966 |  | 
|---|
| 967 | void ShenandoahTraversalGC::weak_refs_work() { | 
|---|
| 968 | assert(_heap->process_references(), "sanity"); | 
|---|
| 969 |  | 
|---|
| 970 | ShenandoahPhaseTimings::Phase phase_root = ShenandoahPhaseTimings::weakrefs; | 
|---|
| 971 |  | 
|---|
| 972 | ShenandoahGCPhase phase(phase_root); | 
|---|
| 973 |  | 
|---|
| 974 | ReferenceProcessor* rp = _heap->ref_processor(); | 
|---|
| 975 |  | 
|---|
| 976 | // NOTE: We cannot shortcut on has_discovered_references() here, because | 
|---|
| 977 | // we will miss marking JNI Weak refs then, see implementation in | 
|---|
| 978 | // ReferenceProcessor::process_discovered_references. | 
|---|
| 979 | weak_refs_work_doit(); | 
|---|
| 980 |  | 
|---|
| 981 | rp->verify_no_references_recorded(); | 
|---|
| 982 | assert(!rp->discovery_enabled(), "Post condition"); | 
|---|
| 983 |  | 
|---|
| 984 | } | 
|---|
| 985 |  | 
|---|
| 986 | class ShenandoahTraversalRefProcTaskProxy : public AbstractGangTask { | 
|---|
| 987 | private: | 
|---|
| 988 | AbstractRefProcTaskExecutor::ProcessTask& _proc_task; | 
|---|
| 989 | ShenandoahTaskTerminator* _terminator; | 
|---|
| 990 |  | 
|---|
| 991 | public: | 
|---|
| 992 | ShenandoahTraversalRefProcTaskProxy(AbstractRefProcTaskExecutor::ProcessTask& proc_task, | 
|---|
| 993 | ShenandoahTaskTerminator* t) : | 
|---|
| 994 | AbstractGangTask( "Process reference objects in parallel"), | 
|---|
| 995 | _proc_task(proc_task), | 
|---|
| 996 | _terminator(t) { | 
|---|
| 997 | } | 
|---|
| 998 |  | 
|---|
| 999 | void work(uint worker_id) { | 
|---|
| 1000 | ShenandoahEvacOOMScope oom_evac_scope; | 
|---|
| 1001 | assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); | 
|---|
| 1002 | ShenandoahHeap* heap = ShenandoahHeap::heap(); | 
|---|
| 1003 | ShenandoahTraversalDrainMarkingStackClosure complete_gc(worker_id, _terminator); | 
|---|
| 1004 |  | 
|---|
| 1005 | ShenandoahForwardedIsAliveClosure is_alive; | 
|---|
| 1006 | if (!heap->is_degenerated_gc_in_progress()) { | 
|---|
| 1007 | ShenandoahTraversalKeepAliveUpdateClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); | 
|---|
| 1008 | _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); | 
|---|
| 1009 | } else { | 
|---|
| 1010 | ShenandoahTraversalKeepAliveUpdateDegenClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); | 
|---|
| 1011 | _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); | 
|---|
| 1012 | } | 
|---|
| 1013 | } | 
|---|
| 1014 | }; | 
|---|
| 1015 |  | 
|---|
| 1016 | class ShenandoahTraversalRefProcTaskExecutor : public AbstractRefProcTaskExecutor { | 
|---|
| 1017 | private: | 
|---|
| 1018 | WorkGang* _workers; | 
|---|
| 1019 |  | 
|---|
| 1020 | public: | 
|---|
| 1021 | ShenandoahTraversalRefProcTaskExecutor(WorkGang* workers) : _workers(workers) {} | 
|---|
| 1022 |  | 
|---|
| 1023 | // Executes a task using worker threads. | 
|---|
| 1024 | void execute(ProcessTask& task, uint ergo_workers) { | 
|---|
| 1025 | assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at a safepoint"); | 
|---|
| 1026 |  | 
|---|
| 1027 | ShenandoahHeap* heap = ShenandoahHeap::heap(); | 
|---|
| 1028 | ShenandoahTraversalGC* traversal_gc = heap->traversal_gc(); | 
|---|
| 1029 | ShenandoahPushWorkerQueuesScope scope(_workers, | 
|---|
| 1030 | traversal_gc->task_queues(), | 
|---|
| 1031 | ergo_workers, | 
|---|
| 1032 | /* do_check = */ false); | 
|---|
| 1033 | uint nworkers = _workers->active_workers(); | 
|---|
| 1034 | traversal_gc->task_queues()->reserve(nworkers); | 
|---|
| 1035 | ShenandoahTaskTerminator terminator(nworkers, traversal_gc->task_queues()); | 
|---|
| 1036 | ShenandoahTraversalRefProcTaskProxy proc_task_proxy(task, &terminator); | 
|---|
| 1037 | _workers->run_task(&proc_task_proxy); | 
|---|
| 1038 | } | 
|---|
| 1039 | }; | 
|---|
| 1040 |  | 
|---|
| 1041 | void ShenandoahTraversalGC::weak_refs_work_doit() { | 
|---|
| 1042 | ReferenceProcessor* rp = _heap->ref_processor(); | 
|---|
| 1043 |  | 
|---|
| 1044 | ShenandoahPhaseTimings::Phase phase_process = ShenandoahPhaseTimings::weakrefs_process; | 
|---|
| 1045 |  | 
|---|
| 1046 | shenandoah_assert_rp_isalive_not_installed(); | 
|---|
| 1047 | ShenandoahForwardedIsAliveClosure is_alive; | 
|---|
| 1048 | ReferenceProcessorIsAliveMutator fix_isalive(rp, &is_alive); | 
|---|
| 1049 |  | 
|---|
| 1050 | WorkGang* workers = _heap->workers(); | 
|---|
| 1051 | uint nworkers = workers->active_workers(); | 
|---|
| 1052 |  | 
|---|
| 1053 | rp->setup_policy(_heap->soft_ref_policy()->should_clear_all_soft_refs()); | 
|---|
| 1054 | rp->set_active_mt_degree(nworkers); | 
|---|
| 1055 |  | 
|---|
| 1056 | assert(task_queues()->is_empty(), "Should be empty"); | 
|---|
| 1057 |  | 
|---|
| 1058 | // complete_gc and keep_alive closures instantiated here are only needed for | 
|---|
| 1059 | // single-threaded path in RP. They share the queue 0 for tracking work, which | 
|---|
| 1060 | // simplifies implementation. Since RP may decide to call complete_gc several | 
|---|
| 1061 | // times, we need to be able to reuse the terminator. | 
|---|
| 1062 | uint serial_worker_id = 0; | 
|---|
| 1063 | ShenandoahTaskTerminator terminator(1, task_queues()); | 
|---|
| 1064 | ShenandoahTraversalSingleThreadedDrainMarkingStackClosure complete_gc(serial_worker_id, &terminator, /* reset_terminator = */ true); | 
|---|
| 1065 | ShenandoahPushWorkerQueuesScope scope(workers, task_queues(), 1, /* do_check = */ false); | 
|---|
| 1066 |  | 
|---|
| 1067 | ShenandoahTraversalRefProcTaskExecutor executor(workers); | 
|---|
| 1068 |  | 
|---|
| 1069 | ReferenceProcessorPhaseTimes pt(_heap->gc_timer(), rp->num_queues()); | 
|---|
| 1070 | if (!_heap->is_degenerated_gc_in_progress()) { | 
|---|
| 1071 | ShenandoahTraversalSingleThreadKeepAliveUpdateClosure keep_alive(task_queues()->queue(serial_worker_id)); | 
|---|
| 1072 | rp->process_discovered_references(&is_alive, &keep_alive, | 
|---|
| 1073 | &complete_gc, &executor, | 
|---|
| 1074 | &pt); | 
|---|
| 1075 | } else { | 
|---|
| 1076 | ShenandoahTraversalSingleThreadKeepAliveUpdateDegenClosure keep_alive(task_queues()->queue(serial_worker_id)); | 
|---|
| 1077 | rp->process_discovered_references(&is_alive, &keep_alive, | 
|---|
| 1078 | &complete_gc, &executor, | 
|---|
| 1079 | &pt); | 
|---|
| 1080 | } | 
|---|
| 1081 |  | 
|---|
| 1082 | pt.print_all_references(); | 
|---|
| 1083 | assert(task_queues()->is_empty() || _heap->cancelled_gc(), "Should be empty"); | 
|---|
| 1084 | } | 
|---|
| 1085 |  | 
|---|