/* * Copyright (c) 2013, 2015, Red Hat, Inc. and/or its affiliates. * * 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. * */ #include "classfile/stringTable.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahConcurrentMark.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" #include "gc/shenandoah/brooksPointer.hpp" #include "gc/shared/referenceProcessor.hpp" #include "code/codeCache.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" #include "gc/shared/taskqueue.inline.hpp" // Mark the object and add it to the queue to be scanned ShenandoahMarkObjsClosure::ShenandoahMarkObjsClosure(SCMObjToScanQueue* q, bool update_refs) : _heap((ShenandoahHeap*)(Universe::heap())), _mark_refs(ShenandoahMarkRefsClosure(q, update_refs)), _live_data(NEW_C_HEAP_ARRAY(size_t, _heap->max_regions(), mtGC)) { Copy::zero_to_bytes(_live_data, _heap->max_regions() * sizeof(size_t)); } ShenandoahMarkObjsClosure::~ShenandoahMarkObjsClosure() { // Merge liveness data back into actual regions. // We need to lock the heap here, to avoid race with growing of heap. MutexLockerEx ml(ShenandoahHeap_lock, true); ShenandoahHeapRegion** regions = _heap->heap_regions(); for (uint i = 0; i < _heap->num_regions(); i++) { regions[i]->increase_live_data(_live_data[i]); } FREE_C_HEAP_ARRAY(size_t, _live_data); } ShenandoahMarkRefsClosure::ShenandoahMarkRefsClosure(SCMObjToScanQueue* q, bool update_refs) : MetadataAwareOopClosure(((ShenandoahHeap *) Universe::heap())->ref_processor()), _queue(q), _heap((ShenandoahHeap*) Universe::heap()), _scm(_heap->concurrentMark()), _update_refs(update_refs) { } void ShenandoahMarkRefsClosure::do_oop(narrowOop* p) { Unimplemented(); } // Walks over all the objects in the generation updating any // references to from space. class CLDMarkAliveClosure : public CLDClosure { private: CLDClosure* _cl; public: CLDMarkAliveClosure(CLDClosure* cl) : _cl(cl) { } void do_cld(ClassLoaderData* cld) { ShenandoahIsAliveClosure is_alive; if (cld->is_alive(&is_alive)) { _cl->do_cld(cld); } } }; class ShenandoahMarkRootsTask : public AbstractGangTask { private: ShenandoahRootProcessor* _rp; bool _update_refs; public: ShenandoahMarkRootsTask(ShenandoahRootProcessor* rp, bool update_refs) : AbstractGangTask("Shenandoah update roots task"), _update_refs(update_refs), _rp(rp) { } void work(uint worker_id) { // tty->print_cr("start mark roots worker: "INT32_FORMAT, worker_id); ShenandoahHeap* heap = ShenandoahHeap::heap(); SCMObjToScanQueue* q = heap->concurrentMark()->get_queue(worker_id); ShenandoahMarkRefsClosure cl(q, _update_refs); CodeBlobToOopClosure blobsCl(&cl, true); CLDToOopClosure cldCl(&cl); ResourceMark m; if (ShenandoahProcessReferences && ClassUnloadingWithConcurrentMark) { _rp->process_strong_roots(&cl, &cldCl, &blobsCl); } else { _rp->process_all_roots(&cl, &cldCl, &blobsCl); } // tty->print_cr("finish mark roots worker: "INT32_FORMAT, worker_id); } }; class SCMConcurrentMarkingTask : public AbstractGangTask { private: ShenandoahConcurrentMark* _cm; ParallelTaskTerminator* _terminator; int _seed; bool _update_refs; public: SCMConcurrentMarkingTask(ShenandoahConcurrentMark* cm, ParallelTaskTerminator* terminator, bool update_refs) : AbstractGangTask("Root Region Scan"), _cm(cm), _terminator(terminator), _update_refs(update_refs), _seed(17) { } void work(uint worker_id) { SCMObjToScanQueue* q = _cm->get_queue(worker_id); ShenandoahMarkObjsClosure cl(q, _update_refs); ShenandoahHeap* heap = ShenandoahHeap::heap(); while (true) { if (heap->cancelled_concgc() || (!_cm->try_queue(q, &cl) && !_cm->try_draining_an_satb_buffer(worker_id) && !_cm->try_to_steal(worker_id, &cl, &_seed)) ) { if (_terminator->offer_termination()) break; } } if (ShenandoahTracePhases && heap->cancelled_concgc()) { tty->print_cr("Cancelled concurrent marking"); } } }; void ShenandoahConcurrentMark::prepare_unmarked_root_objs() { ShenandoahHeap* heap = ShenandoahHeap::heap(); bool update_refs = heap->need_update_refs(); if (update_refs) { COMPILER2_PRESENT(DerivedPointerTable::clear()); } prepare_unmarked_root_objs_no_derived_ptrs(update_refs); if (update_refs) { COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); } } void ShenandoahConcurrentMark::prepare_unmarked_root_objs_no_derived_ptrs(bool update_refs) { assert(Thread::current()->is_VM_thread(), "can only do this in VMThread"); ShenandoahHeap* heap = ShenandoahHeap::heap(); if (ShenandoahParallelRootScan) { ClassLoaderDataGraph::clear_claimed_marks(); heap->conc_workers()->set_active_workers(_max_conc_worker_id); ShenandoahRootProcessor root_proc(heap, _max_conc_worker_id); TASKQUEUE_STATS_ONLY(reset_taskqueue_stats()); ShenandoahMarkRootsTask mark_roots(&root_proc, update_refs); heap->conc_workers()->run_task(&mark_roots); // Mark through any class loaders that have been found alive. ShenandoahMarkRefsClosure cl(get_queue(0), update_refs); CLDToOopClosure cldCl(&cl); CLDMarkAliveClosure cld_keep_alive(&cldCl); ClassLoaderDataGraph::roots_cld_do(NULL, &cld_keep_alive); } else { ShenandoahMarkRefsClosure cl(get_queue(0), update_refs); heap->roots_iterate(&cl); } if (!(ShenandoahProcessReferences && ClassUnloadingWithConcurrentMark)) { ShenandoahMarkRefsClosure cl(get_queue(0), update_refs); heap->weak_roots_iterate(&cl); } // tty->print_cr("all root marker threads done"); } void ShenandoahConcurrentMark::initialize() { _max_conc_worker_id = MAX2((uint) ConcGCThreads, 1U); _task_queues = new SCMObjToScanQueueSet((int) _max_conc_worker_id); for (uint i = 0; i < _max_conc_worker_id; ++i) { SCMObjToScanQueue* task_queue = new SCMObjToScanQueue(); task_queue->initialize(); _task_queues->register_queue(i, task_queue); } JavaThread::satb_mark_queue_set().set_buffer_size(1014 /* G1SATBBufferSize */); } void ShenandoahConcurrentMark::mark_from_roots() { if (ShenandoahGCVerbose) { tty->print_cr("STOPPING THE WORLD: before marking"); tty->print_cr("Starting markFromRoots"); } ShenandoahHeap* sh = (ShenandoahHeap *) Universe::heap(); bool update_refs = sh->need_update_refs(); sh->shenandoahPolicy()->record_phase_start(ShenandoahCollectorPolicy::conc_mark); ParallelTaskTerminator terminator(_max_conc_worker_id, _task_queues); if (ShenandoahProcessReferences) { ReferenceProcessor* rp = sh->ref_processor(); // enable ("weak") refs discovery rp->enable_discovery(true /*verify_no_refs*/); rp->setup_policy(false); // snapshot the soft ref policy to be used in this cycle } SCMConcurrentMarkingTask markingTask = SCMConcurrentMarkingTask(this, &terminator, update_refs); sh->conc_workers()->set_active_workers(_max_conc_worker_id); sh->conc_workers()->run_task(&markingTask); if (ShenandoahGCVerbose) { tty->print("total workers = %u active workers = %u\n", sh->conc_workers()->total_workers(), sh->conc_workers()->active_workers()); TASKQUEUE_STATS_ONLY(print_taskqueue_stats()); TASKQUEUE_STATS_ONLY(reset_taskqueue_stats()); } if (ShenandoahGCVerbose) { tty->print_cr("Finishing markFromRoots"); tty->print_cr("RESUMING THE WORLD: after marking"); } sh->shenandoahPolicy()->record_phase_end(ShenandoahCollectorPolicy::conc_mark); } class FinishDrainSATBBuffersTask : public AbstractGangTask { private: ShenandoahConcurrentMark* _cm; ParallelTaskTerminator* _terminator; public: FinishDrainSATBBuffersTask(ShenandoahConcurrentMark* cm, ParallelTaskTerminator* terminator) : AbstractGangTask("Finish draining SATB buffers"), _cm(cm), _terminator(terminator) { } void work(uint worker_id) { _cm->drain_satb_buffers(worker_id, true); } }; class ShenandoahUpdateAliveRefs : public OopClosure { private: ShenandoahHeap* _heap; public: ShenandoahUpdateAliveRefs() : _heap(ShenandoahHeap::heap()) { } virtual void do_oop(oop* p) { _heap->maybe_update_oop_ref(p); } virtual void do_oop(narrowOop* p) { Unimplemented(); } }; void ShenandoahConcurrentMark::finish_mark_from_roots() { if (ShenandoahGCVerbose) { tty->print_cr("Starting finishMarkFromRoots"); } IsGCActiveMark is_active; ShenandoahHeap* sh = (ShenandoahHeap *) Universe::heap(); // Trace any (new) unmarked root references. sh->shenandoahPolicy()->record_phase_start(ShenandoahCollectorPolicy::rescan_roots); prepare_unmarked_root_objs(); sh->shenandoahPolicy()->record_phase_end(ShenandoahCollectorPolicy::rescan_roots); sh->shenandoahPolicy()->record_phase_start(ShenandoahCollectorPolicy::drain_satb); { StrongRootsScope scope(_max_conc_worker_id); ParallelTaskTerminator terminator(_max_conc_worker_id, _task_queues); // drain_satb_buffers(0, true); FinishDrainSATBBuffersTask drain_satb_buffers(this, &terminator); sh->conc_workers()->set_active_workers(_max_conc_worker_id); sh->conc_workers()->run_task(&drain_satb_buffers); sh->shenandoahPolicy()->record_phase_end(ShenandoahCollectorPolicy::drain_satb); } // Finally mark everything else we've got in our queues during the previous steps. { sh->shenandoahPolicy()->record_phase_start(ShenandoahCollectorPolicy::drain_queues); ParallelTaskTerminator terminator(_max_conc_worker_id, _task_queues); SCMConcurrentMarkingTask markingTask = SCMConcurrentMarkingTask(this, &terminator, sh->need_update_refs()); sh->conc_workers()->set_active_workers(_max_conc_worker_id); sh->conc_workers()->run_task(&markingTask); sh->shenandoahPolicy()->record_phase_end(ShenandoahCollectorPolicy::drain_queues); } #ifdef ASSERT for (int i = 0; i < (int) _max_conc_worker_id; i++) { assert(_task_queues->queue(i)->is_empty(), "Should be empty"); } #endif // When we're done marking everything, we process weak references. if (ShenandoahProcessReferences) { sh->shenandoahPolicy()->record_phase_start(ShenandoahCollectorPolicy::weakrefs); weak_refs_work(); sh->shenandoahPolicy()->record_phase_end(ShenandoahCollectorPolicy::weakrefs); } #ifdef ASSERT for (int i = 0; i < (int) _max_conc_worker_id; i++) { assert(_task_queues->queue(i)->is_empty(), "Should be empty"); } #endif if (ShenandoahGCVerbose) { tty->print_cr("Finishing finishMarkFromRoots"); #ifdef SLOWDEBUG for (int i = 0; i <(int)_max_conc_worker_id; i++) { tty->print("Queue: "INT32_FORMAT":", i); _task_queues->queue(i)->stats.print(tty, 10); tty->cr(); _task_queues->queue(i)->stats.verify(); } #endif } // We still need to update (without marking) alive refs in JNI handles. if (ShenandoahProcessReferences && ClassUnloadingWithConcurrentMark) { ShenandoahUpdateAliveRefs cl; ShenandoahIsAliveClosure is_alive; JNIHandles::weak_oops_do(&is_alive, &cl); } #ifdef ASSERT verify_roots(); if (ShenandoahDumpHeapAfterConcurrentMark) { sh->ensure_parsability(false); sh->print_all_refs("post-mark"); } #endif } #ifdef ASSERT void ShenandoahVerifyRootsClosure1::do_oop(oop* p) { oop obj = oopDesc::load_heap_oop(p); if (! oopDesc::is_null(obj)) { guarantee(ShenandoahHeap::heap()->is_marked_current(obj), "oop must be marked"); guarantee(obj == ShenandoahBarrierSet::resolve_oop_static_not_null(obj), "oop must not be forwarded"); } } void ShenandoahConcurrentMark::verify_roots() { ShenandoahVerifyRootsClosure1 cl; CodeBlobToOopClosure blobsCl(&cl, true); CLDToOopClosure cldCl(&cl); ClassLoaderDataGraph::clear_claimed_marks(); ShenandoahRootProcessor rp(ShenandoahHeap::heap(), 1); rp.process_roots(&cl, &cl, &cldCl, &cldCl, &cldCl, &blobsCl); } #endif class ShenandoahSATBBufferClosure : public SATBBufferClosure { private: SCMObjToScanQueue* _queue; ShenandoahHeap* _heap; public: ShenandoahSATBBufferClosure(SCMObjToScanQueue* q) : _queue(q), _heap(ShenandoahHeap::heap()) { } void do_buffer(void** buffer, size_t size) { // tty->print_cr("draining one satb buffer"); for (size_t i = 0; i < size; ++i) { void* entry = buffer[i]; oop obj = oop(entry); // tty->print_cr("satb buffer entry: "PTR_FORMAT, p2i((HeapWord*) obj)); if (!oopDesc::is_null(obj)) { obj = ShenandoahBarrierSet::resolve_oop_static_not_null(obj); if (_heap->mark_current(obj)) { bool pushed = _queue->push(obj); assert(pushed, "overflow queue should always succeed pushing"); } } } } }; class ShenandoahSATBThreadsClosure : public ThreadClosure { ShenandoahSATBBufferClosure* _satb_cl; int _thread_parity; public: ShenandoahSATBThreadsClosure(ShenandoahSATBBufferClosure* satb_cl) : _satb_cl(satb_cl), _thread_parity(Threads::thread_claim_parity()) {} void do_thread(Thread* thread) { if (thread->is_Java_thread()) { if (thread->claim_oops_do(true, _thread_parity)) { JavaThread* jt = (JavaThread*)thread; jt->satb_mark_queue().apply_closure_and_empty(_satb_cl); } } else if (thread->is_VM_thread()) { if (thread->claim_oops_do(true, _thread_parity)) { JavaThread::satb_mark_queue_set().shared_satb_queue()->apply_closure_and_empty(_satb_cl); } } } }; void ShenandoahConcurrentMark::drain_satb_buffers(uint worker_id, bool remark) { // tty->print_cr("start draining SATB buffers"); ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); SCMObjToScanQueue* q = get_queue(worker_id); ShenandoahSATBBufferClosure cl(q); SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); while (satb_mq_set.apply_closure_to_completed_buffer(&cl)); if (remark) { ShenandoahSATBThreadsClosure tc(&cl); Threads::threads_do(&tc); } // tty->print_cr("end draining SATB buffers"); } bool ShenandoahConcurrentMark::drain_one_satb_buffer(uint worker_id) { ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); SCMObjToScanQueue* q = get_queue(worker_id); ShenandoahSATBBufferClosure cl(q); SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); bool result = satb_mq_set.apply_closure_to_completed_buffer(&cl); return result; } #if TASKQUEUE_STATS void ShenandoahConcurrentMark::print_taskqueue_stats_hdr(outputStream* const st) { st->print_raw_cr("GC Task Stats"); st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr(); st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr(); } void ShenandoahConcurrentMark::print_taskqueue_stats(outputStream* const st) const { print_taskqueue_stats_hdr(st); ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); TaskQueueStats totals; const int n = sh->max_conc_workers(); for (int i = 0; i < n; ++i) { st->print(INT32_FORMAT_W(3), i); _task_queues->queue(i)->stats.print(st); st->print("\n"); totals += _task_queues->queue(i)->stats; } st->print_raw("tot "); totals.print(st); st->cr(); DEBUG_ONLY(totals.verify()); } void ShenandoahConcurrentMark::print_push_only_taskqueue_stats(outputStream* const st) const { print_taskqueue_stats_hdr(st); ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); TaskQueueStats totals; const int n = sh->max_conc_workers(); for (int i = 0; i < n; ++i) { st->print(INT32_FORMAT_W(3), i); _task_queues->queue(i)->stats.print(st); st->print("\n"); totals += _task_queues->queue(i)->stats; } st->print_raw("tot "); totals.print(st); st->cr(); } void ShenandoahConcurrentMark::reset_taskqueue_stats() { ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); const int n = sh->max_conc_workers(); for (int i = 0; i < n; ++i) { _task_queues->queue(i)->stats.reset(); } } #endif // TASKQUEUE_STATS // Weak Reference Closures class ShenandoahCMDrainMarkingStackClosure: public VoidClosure { ShenandoahHeap* _sh; ShenandoahConcurrentMark* _scm; uint _worker_id; int _seed; public: ShenandoahCMDrainMarkingStackClosure(uint worker_id): _worker_id(worker_id), _seed(17) { _sh = (ShenandoahHeap*) Universe::heap(); _scm = _sh->concurrentMark(); } void do_void() { SCMObjToScanQueue* q = _scm->get_queue(_worker_id); ShenandoahMarkObjsClosure cl(q, _sh->need_update_refs()); while (true) { if (!_scm->try_queue(q, &cl) && !_scm->try_draining_an_satb_buffer(_worker_id) && !_scm->try_to_steal(_worker_id, &cl, &_seed)) { break; } } } }; class ShenandoahCMKeepAliveAndDrainClosure: public OopClosure { SCMObjToScanQueue* _queue; ShenandoahHeap* _sh; ShenandoahConcurrentMark* _scm; size_t _ref_count; public: ShenandoahCMKeepAliveAndDrainClosure(SCMObjToScanQueue* q) : _queue(q) { _sh = (ShenandoahHeap*) Universe::heap(); _scm = _sh->concurrentMark(); _ref_count = 0; } virtual void do_oop(oop* p){ do_oop_work(p);} virtual void do_oop(narrowOop* p) { assert(false, "narrowOops Aren't implemented"); } void do_oop_work(oop* p) { oop obj; if (_sh->need_update_refs()) { obj = _sh->maybe_update_oop_ref(p); } else { obj = oopDesc::load_heap_oop(p); } assert(obj == oopDesc::bs()->read_barrier(obj), "only get updated oops in weak ref processing"); if (obj != NULL) { if (Verbose && ShenandoahTraceWeakReferences) { gclog_or_tty->print_cr("\twe're looking at location " "*"PTR_FORMAT" = "PTR_FORMAT, p2i(p), p2i((void*) obj)); obj->print(); } if (_sh->mark_current(obj)) { bool pushed = _queue->push(obj); assert(pushed, "overflow queue should always succeed pushing"); } _ref_count++; } } size_t ref_count() { return _ref_count; } }; class ShenandoahRefProcTaskProxy : public AbstractGangTask { private: AbstractRefProcTaskExecutor::ProcessTask& _proc_task; public: ShenandoahRefProcTaskProxy(AbstractRefProcTaskExecutor::ProcessTask& proc_task) : AbstractGangTask("Process reference objects in parallel"), _proc_task(proc_task) { } void work(uint worker_id) { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahIsAliveClosure is_alive; ShenandoahCMKeepAliveAndDrainClosure keep_alive(heap->concurrentMark()->get_queue(worker_id)); ShenandoahCMDrainMarkingStackClosure complete_gc(worker_id); _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); } }; class ShenandoahRefEnqueueTaskProxy : public AbstractGangTask { private: AbstractRefProcTaskExecutor::EnqueueTask& _enqueue_task; public: ShenandoahRefEnqueueTaskProxy(AbstractRefProcTaskExecutor::EnqueueTask& enqueue_task) : AbstractGangTask("Enqueue reference objects in parallel"), _enqueue_task(enqueue_task) { } void work(uint worker_id) { _enqueue_task.work(worker_id); } }; class ShenandoahRefProcTaskExecutor : public AbstractRefProcTaskExecutor { private: WorkGang* _workers; public: ShenandoahRefProcTaskExecutor() : _workers(ShenandoahHeap::heap()->conc_workers()) { } // Executes a task using worker threads. void execute(ProcessTask& task) { ShenandoahRefProcTaskProxy proc_task_proxy(task); _workers->run_task(&proc_task_proxy); } void execute(EnqueueTask& task) { ShenandoahRefEnqueueTaskProxy enqueue_task_proxy(task); _workers->run_task(&enqueue_task_proxy); } }; void ShenandoahConcurrentMark::weak_refs_work() { ShenandoahHeap* sh = (ShenandoahHeap*) Universe::heap(); ReferenceProcessor* rp = sh->ref_processor(); // Setup collector policy for softref cleaning. bool clear_soft_refs = sh->collector_policy()->use_should_clear_all_soft_refs(true /* bogus arg*/); if (ShenandoahTraceWeakReferences) { tty->print_cr("clearing soft refs: %s", BOOL_TO_STR(clear_soft_refs)); } rp->setup_policy(clear_soft_refs); uint serial_worker_id = 0; ShenandoahIsAliveClosure is_alive; ShenandoahCMKeepAliveAndDrainClosure keep_alive(get_queue(serial_worker_id)); ShenandoahCMDrainMarkingStackClosure complete_gc(serial_worker_id); ShenandoahRefProcTaskExecutor par_task_executor; bool processing_is_mt = true; AbstractRefProcTaskExecutor* executor = (processing_is_mt ? &par_task_executor : NULL); if (ShenandoahTraceWeakReferences) { gclog_or_tty->print_cr("start processing references"); } rp->process_discovered_references(&is_alive, &keep_alive, &complete_gc, &par_task_executor, NULL, ShenandoahHeap::heap()->tracer()->gc_id()); if (ShenandoahTraceWeakReferences) { gclog_or_tty->print_cr("finished processing references, processed "SIZE_FORMAT" refs", keep_alive.ref_count()); gclog_or_tty->print_cr("start enqueuing references"); } rp->enqueue_discovered_references(executor); if (ShenandoahTraceWeakReferences) { gclog_or_tty->print_cr("finished enqueueing references"); } rp->verify_no_references_recorded(); assert(!rp->discovery_enabled(), "Post condition"); if (ClassUnloadingWithConcurrentMark) { // Unload classes and purge SystemDictionary. bool purged_class = SystemDictionary::do_unloading(&is_alive); // Unload nmethods. CodeCache::do_unloading(&is_alive, purged_class); // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(&is_alive); // Delete entries from dead interned strings. StringTable::unlink(&is_alive); // Clean up unreferenced symbols in symbol table. SymbolTable::unlink(); ClassLoaderDataGraph::purge(); } } void ShenandoahConcurrentMark::cancel() { ShenandoahHeap* sh = ShenandoahHeap::heap(); // Cancel weak-ref discovery. if (ShenandoahProcessReferences) { ReferenceProcessor* rp = sh->ref_processor(); rp->abandon_partial_discovery(); rp->disable_discovery(); } // Clean up marking stacks. SCMObjToScanQueueSet* queues = task_queues(); for (uint i = 0; i < _max_conc_worker_id; ++i) { SCMObjToScanQueue* task_queue = queues->queue(i); task_queue->set_empty(); task_queue->overflow_stack()->clear(); } // Cancel SATB buffers. JavaThread::satb_mark_queue_set().abandon_partial_marking(); } SCMObjToScanQueue* ShenandoahConcurrentMark::get_queue(uint worker_id) { worker_id = worker_id % _max_conc_worker_id; return _task_queues->queue(worker_id); }