< prev index next >

src/hotspot/share/gc/parallel/psScavenge.cpp

8224663: Parallel GC: Use WorkGang (5: ScavengeRootsTask)
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "aot/aotLoader.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/stringTable.hpp"
 #include "code/codeCache.hpp"
 #include "gc/parallel/gcTaskManager.hpp"
 #include "gc/parallel/parallelScavengeHeap.hpp"
 #include "gc/parallel/psAdaptiveSizePolicy.hpp"
 #include "gc/parallel/psClosure.inline.hpp"
 #include "gc/parallel/psMarkSweepProxy.hpp"
 #include "gc/parallel/psParallelCompact.inline.hpp"
 #include "gc/parallel/psPromotionManager.inline.hpp"
+#include "gc/parallel/psRootType.inline.hpp"
 #include "gc/parallel/psScavenge.inline.hpp"
 #include "gc/parallel/psTasks.hpp"
 #include "gc/shared/gcCause.hpp"
 #include "gc/shared/gcHeapSummary.hpp"
 #include "gc/shared/gcId.hpp"

@@ -43,13 +46,18 #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" +#include "gc/shared/scavengableNMethods.hpp" #include "gc/shared/spaceDecorator.hpp" #include "gc/shared/weakProcessor.hpp" #include "gc/shared/workerPolicy.hpp" +#include "gc/shared/workgroup.hpp" +#if INCLUDE_JVMCI +#include "jvmci/jvmci.hpp" +#endif #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "logging/log.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp"
@@ -57,13 +65,15 #include "runtime/biasedLocking.hpp" #include "runtime/handles.inline.hpp" #include "runtime/threadCritical.hpp" #include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "services/management.hpp" #include "services/memoryService.hpp" #include "utilities/stack.inline.hpp" + HeapWord* PSScavenge::_to_space_top_before_gc = NULL; int PSScavenge::_consecutive_skipped_scavenges = 0; SpanSubjectToDiscoveryClosure PSScavenge::_span_based_discoverer; ReferenceProcessor* PSScavenge::_ref_processor = NULL; PSCardTable* PSScavenge::_card_table = NULL;
@@ -74,10 +84,97 elapsedTimer PSScavenge::_accumulated_time; STWGCTimer PSScavenge::_gc_timer; ParallelScavengeTracer PSScavenge::_gc_tracer; CollectorCounters* PSScavenge::_counters = NULL; +void scavenge_roots_task(Parallel::RootType::Value root_type, uint which) { + assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); + PSScavengeRootsClosure roots_closure(pm); + PSPromoteRootsClosure roots_to_old_closure(pm); + + switch (root_type) { + case Parallel::RootType::universe: + Universe::oops_do(&roots_closure); + break; + + case Parallel::RootType::jni_handles: + JNIHandles::oops_do(&roots_closure); + break; + + case Parallel::RootType::object_synchronizer: + ObjectSynchronizer::oops_do(&roots_closure); + break; + + case Parallel::RootType::system_dictionary: + SystemDictionary::oops_do(&roots_closure); + break; + + case Parallel::RootType::class_loader_data: + { + PSScavengeCLDClosure cld_closure(pm); + ClassLoaderDataGraph::cld_do(&cld_closure); + } + break; + + case Parallel::RootType::management: + Management::oops_do(&roots_closure); + break; + + case Parallel::RootType::jvmti: + JvmtiExport::oops_do(&roots_closure); + break; + + case Parallel::RootType::code_cache: + { + MarkingCodeBlobClosure code_closure(&roots_to_old_closure, CodeBlobToOopClosure::FixRelocations); + ScavengableNMethods::nmethods_do(&code_closure); + AOTLoader::oops_do(&roots_closure); + } + break; + +#if INCLUDE_JVMCI + case Parallel::RootType::jvmci: + JVMCI::oops_do(&roots_closure); + break; +#endif + + case Parallel::RootType::sentinel: + DEBUG_ONLY(default:) // DEBUG_ONLY hack will create compile error on release builds (-Wswitch) and runtime check on debug builds + fatal("Bad enumeration value: %u", root_type); + break; + } + + // Do the real work + pm->drain_stacks(false); +} + +void steal_task(ParallelTaskTerminator& terminator, uint worker_id) { + assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = + PSPromotionManager::gc_thread_promotion_manager(worker_id); + pm->drain_stacks(true); + guarantee(pm->stacks_empty(), + "stacks should be empty at this point"); + + while (true) { + StarTask p; + if (PSPromotionManager::steal_depth(worker_id, p)) { + TASKQUEUE_STATS_ONLY(pm->record_steal(p)); + pm->process_popped_location_depth(p); + pm->drain_stacks_depth(true); + } else { + if (terminator.offer_termination()) { + break; + } + } + } + guarantee(pm->stacks_empty(), "stacks should be empty at this point"); +} + // Define before use class PSIsAliveClosure: public BoolObjectClosure { public: bool do_object_b(oop p) { return (!PSScavenge::is_obj_in_young(p)) || p->is_forwarded();
@@ -220,18 +317,143 } return full_gc_done; } -class PSAddThreadRootsTaskClosure : public ThreadClosure { -private: - GCTaskQueue* _q; +class PSThreadRootsTaskClosure : public ThreadClosure { + uint _worker_id; +public: + PSThreadRootsTaskClosure(uint worker_id) : _worker_id(worker_id) { } + virtual void do_thread(Thread* thread) { + assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc"); + + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(_worker_id); + PSScavengeRootsClosure roots_closure(pm); + MarkingCodeBlobClosure roots_in_blobs(&roots_closure, CodeBlobToOopClosure::FixRelocations); + + thread->oops_do(&roots_closure, &roots_in_blobs); + + // Do the real work + pm->drain_stacks(false); + } +}; +// +// OldToYoungRootsTask +// +// This task is used to scan old to young roots in parallel +// +// A GC thread executing this tasks divides the generation (old gen) +// into slices and takes a stripe in the slice as its part of the +// work. +// +// +===============+ slice 0 +// | stripe 0 | +// +---------------+ +// | stripe 1 | +// +---------------+ +// | stripe 2 | +// +---------------+ +// | stripe 3 | +// +===============+ slice 1 +// | stripe 0 | +// +---------------+ +// | stripe 1 | +// +---------------+ +// | stripe 2 | +// +---------------+ +// | stripe 3 | +// +===============+ slice 2 +// ... +// +// A task is created for each stripe. In this case there are 4 tasks +// created. A GC thread first works on its stripe within slice 0 +// and then moves to its stripe in the next slice until all stripes +// exceed the top of the generation. Note that having fewer GC threads +// than stripes works because all the tasks are executed so all stripes +// will be covered. In this example if 4 tasks have been created to cover +// all the stripes and there are only 3 threads, one of the threads will +// get the tasks with the 4th stripe. However, there is a dependence in +// PSCardTable::scavenge_contents_parallel() on the number +// of tasks created. In scavenge_contents_parallel the distance +// to the next stripe is calculated based on the number of tasks. +// If the stripe width is ssize, a task's next stripe is at +// ssize * number_of_tasks (= slice_stride). In this case after +// finishing stripe 0 in slice 0, the thread finds the stripe 0 in slice1 +// by adding slice_stride to the start of stripe 0 in slice 0 to get +// to the start of stride 0 in slice 1. + +class ScavengeRootsTask : public AbstractGangTask { + StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do + EnumClaimer<Parallel::RootType::Value> _enum_claimer; + PSOldGen* _old_gen; + HeapWord* _gen_top; + uint _active_workers; + bool _is_empty; + TaskTerminator _terminator; public: - PSAddThreadRootsTaskClosure(GCTaskQueue* q) : _q(q) { } - void do_thread(Thread* t) { - _q->enqueue(new ThreadRootsTask(t)); + ScavengeRootsTask( + PSOldGen* old_gen, + HeapWord* gen_top, + uint active_workers, + bool is_empty) + : AbstractGangTask("ScavengeRootsTask"), + _strong_roots_scope(active_workers), + _enum_claimer(Parallel::RootType::sentinel), + _old_gen(old_gen), + _gen_top(gen_top), + _active_workers(active_workers), + _is_empty(is_empty), + _terminator(active_workers, PSPromotionManager::vm_thread_promotion_manager()->stack_array_depth()) { + } + + virtual void work(uint worker_id) { + ResourceMark rm; + + if (!_is_empty) { + // There are only old-to-young pointers if there are objects + // in the old gen. + + // There are not old-to-young pointers if the old gen is empty. + assert(!_old_gen->object_space()->is_empty(), + "Should not be called is there is no work"); + assert(_old_gen != NULL, "Sanity"); + assert(_old_gen->object_space()->contains(_gen_top) || _gen_top == _old_gen->object_space()->top(), "Sanity"); + assert(worker_id < ParallelGCThreads, "Sanity"); + + { + PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(worker_id); + PSCardTable* card_table = ParallelScavengeHeap::heap()->card_table(); + + card_table->scavenge_contents_parallel(_old_gen->start_array(), + _old_gen->object_space(), + _gen_top, + pm, + worker_id, + _active_workers); + + // Do the real work + pm->drain_stacks(false); + } + } + + for (Parallel::RootType::Value root_type; _enum_claimer.try_claim(root_type); /* empty */) { + scavenge_roots_task(root_type, worker_id); + } + + PSThreadRootsTaskClosure closure(worker_id); + Threads::possibly_parallel_threads_do(true /*parallel */, &closure); + + + // If active_workers can exceed 1, add a StrealTask. + // PSPromotionManager::drain_stacks_depth() does not fully drain its + // stacks and expects a StealTask to complete the draining if + // ParallelGCThreads is > 1. + + if (_active_workers > 1) { + steal_task(*_terminator.terminator() , worker_id); + } } }; // This method contains no policy. You should probably // be calling invoke() instead.
@@ -358,49 +580,13 // We'll use the promotion manager again later. PSPromotionManager* promotion_manager = PSPromotionManager::vm_thread_promotion_manager(); { GCTraceTime(Debug, gc, phases) tm("Scavenge", &_gc_timer); - ParallelScavengeHeap::ParStrongRootsScope psrs; - - GCTaskQueue* q = GCTaskQueue::create(); - - if (!old_gen->object_space()->is_empty()) { - // There are only old-to-young pointers if there are objects - // in the old gen. - uint stripe_total = active_workers; - for(uint i=0; i < stripe_total; i++) { - q->enqueue(new OldToYoungRootsTask(old_gen, old_top, i, stripe_total)); - } - } - - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::universe)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jni_handles)); - // We scan the thread roots in parallel - PSAddThreadRootsTaskClosure cl(q); - Threads::java_threads_and_vm_thread_do(&cl); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::object_synchronizer)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::management)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::system_dictionary)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::class_loader_data)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti)); - q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache)); - JVMCI_ONLY(q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmci));) - - TaskTerminator terminator(active_workers, - (TaskQueueSetSuper*) promotion_manager->stack_array_depth()); - // If active_workers can exceed 1, add a StrealTask. - // PSPromotionManager::drain_stacks_depth() does not fully drain its - // stacks and expects a StealTask to complete the draining if - // ParallelGCThreads is > 1. - if (gc_task_manager()->workers() > 1) { - for (uint j = 0; j < active_workers; j++) { - q->enqueue(new StealTask(terminator.terminator())); - } - } - gc_task_manager()->execute_and_wait(q); + ScavengeRootsTask task(old_gen, old_top, active_workers, old_gen->object_space()->is_empty()); + ParallelScavengeHeap::heap()->workers().run_task(&task); } scavenge_midpoint.update(); // Process reference objects discovered during scavenge
< prev index next >