8231672: Simplify the reference processing parallelization framework

Reviewed-by: tschatzl, ayang
This commit is contained in:
Leo Korinth 2021-05-19 09:39:40 +00:00
parent 392f962e0e
commit 6ef46ce386
20 changed files with 387 additions and 731 deletions

View File

@ -3051,7 +3051,7 @@ bool G1ParEvacuateFollowersClosure::offer_termination() {
EventGCPhaseParallel event;
G1ParScanThreadState* const pss = par_scan_state();
start_term_time();
const bool res = terminator()->offer_termination();
const bool res = (terminator() == nullptr) ? true : terminator()->offer_termination();
end_term_time();
event.commit(GCId::current(), pss->worker_id(), G1GCPhaseTimes::phase_name(G1GCPhaseTimes::Termination));
return res;
@ -3182,99 +3182,35 @@ public:
}
};
// Parallel Reference Processing closures
// Implementation of AbstractRefProcTaskExecutor for parallel reference
// processing during G1 evacuation pauses.
class G1STWRefProcTaskExecutor: public AbstractRefProcTaskExecutor {
private:
G1CollectedHeap* _g1h;
G1ParScanThreadStateSet* _pss;
G1ScannerTasksQueueSet* _queues;
WorkGang* _workers;
class G1STWRefProcProxyTask : public RefProcProxyTask {
G1CollectedHeap& _g1h;
G1ParScanThreadStateSet& _pss;
TaskTerminator _terminator;
G1ScannerTasksQueueSet& _task_queues;
public:
G1STWRefProcTaskExecutor(G1CollectedHeap* g1h,
G1ParScanThreadStateSet* per_thread_states,
WorkGang* workers,
G1ScannerTasksQueueSet *task_queues) :
_g1h(g1h),
_pss(per_thread_states),
_queues(task_queues),
_workers(workers)
{
g1h->ref_processor_stw()->set_active_mt_degree(workers->active_workers());
G1STWRefProcProxyTask(uint max_workers, G1CollectedHeap& g1h, G1ParScanThreadStateSet& pss, G1ScannerTasksQueueSet& task_queues)
: RefProcProxyTask("G1STWRefProcProxyTask", max_workers),
_g1h(g1h),
_pss(pss),
_terminator(max_workers, &task_queues),
_task_queues(task_queues) {}
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
_pss.state_for_worker(index)->set_ref_discoverer(nullptr);
G1STWIsAliveClosure is_alive(&_g1h);
G1CopyingKeepAliveClosure keep_alive(&_g1h, _pss.state_for_worker(index));
G1ParEvacuateFollowersClosure complete_gc(&_g1h, _pss.state_for_worker(index), &_task_queues, _tm == RefProcThreadModel::Single ? nullptr : &_terminator, G1GCPhaseTimes::ObjCopy);
_rp_task->rp_work(worker_id, &is_alive, &keep_alive, &complete_gc);
}
// Executes the given task using concurrent marking worker threads.
virtual void execute(ProcessTask& task, uint ergo_workers);
};
// Gang task for possibly parallel reference processing
class G1STWRefProcTaskProxy: public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
ProcessTask& _proc_task;
G1CollectedHeap* _g1h;
G1ParScanThreadStateSet* _pss;
G1ScannerTasksQueueSet* _task_queues;
TaskTerminator* _terminator;
public:
G1STWRefProcTaskProxy(ProcessTask& proc_task,
G1CollectedHeap* g1h,
G1ParScanThreadStateSet* per_thread_states,
G1ScannerTasksQueueSet *task_queues,
TaskTerminator* terminator) :
AbstractGangTask("Process reference objects in parallel"),
_proc_task(proc_task),
_g1h(g1h),
_pss(per_thread_states),
_task_queues(task_queues),
_terminator(terminator)
{}
virtual void work(uint worker_id) {
// The reference processing task executed by a single worker.
ResourceMark rm;
G1STWIsAliveClosure is_alive(_g1h);
G1ParScanThreadState* pss = _pss->state_for_worker(worker_id);
pss->set_ref_discoverer(NULL);
// Keep alive closure.
G1CopyingKeepAliveClosure keep_alive(_g1h, pss);
// Complete GC closure
G1ParEvacuateFollowersClosure drain_queue(_g1h, pss, _task_queues, _terminator, G1GCPhaseTimes::ObjCopy);
// Call the reference processing task's work routine.
_proc_task.work(worker_id, is_alive, keep_alive, drain_queue);
// Note we cannot assert that the refs array is empty here as not all
// of the processing tasks (specifically phase2 - pp2_work) execute
// the complete_gc closure (which ordinarily would drain the queue) so
// the queue may not be empty.
void prepare_run_task_hook() override {
_terminator.reset_for_reuse(_queue_count);
}
};
// Driver routine for parallel reference processing.
// Creates an instance of the ref processing gang
// task and has the worker threads execute it.
void G1STWRefProcTaskExecutor::execute(ProcessTask& proc_task, uint ergo_workers) {
assert(_workers != NULL, "Need parallel worker threads.");
assert(_workers->active_workers() >= ergo_workers,
"Ergonomically chosen workers (%u) should be less than or equal to active workers (%u)",
ergo_workers, _workers->active_workers());
TaskTerminator terminator(ergo_workers, _queues);
G1STWRefProcTaskProxy proc_task_proxy(proc_task, _g1h, _pss, _queues, &terminator);
_workers->run_task(&proc_task_proxy, ergo_workers);
}
// End of weak reference support closures
void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per_thread_states) {
@ -3283,53 +3219,27 @@ void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per
ReferenceProcessor* rp = _ref_processor_stw;
assert(rp->discovery_enabled(), "should have been enabled");
// Closure to test whether a referent is alive.
G1STWIsAliveClosure is_alive(this);
// Even when parallel reference processing is enabled, the processing
// of JNI refs is serial and performed serially by the current thread
// rather than by a worker. The following PSS will be used for processing
// JNI refs.
// Use only a single queue for this PSS.
G1ParScanThreadState* pss = per_thread_states->state_for_worker(0);
pss->set_ref_discoverer(NULL);
assert(pss->queue_is_empty(), "pre-condition");
// Keep alive closure.
G1CopyingKeepAliveClosure keep_alive(this, pss);
// Serial Complete GC closure
G1STWDrainQueueClosure drain_queue(this, pss);
// Setup the soft refs policy...
rp->setup_policy(false);
ReferenceProcessorPhaseTimes* pt = phase_times()->ref_phase_times();
ReferenceProcessorPhaseTimes& pt = *phase_times()->ref_phase_times();
ReferenceProcessorStats stats;
if (!rp->processing_is_mt()) {
// Serial reference processing...
stats = rp->process_discovered_references(&is_alive,
&keep_alive,
&drain_queue,
NULL,
pt);
} else {
uint no_of_gc_workers = workers()->active_workers();
uint no_of_gc_workers = workers()->active_workers();
// Parallel reference processing
assert(no_of_gc_workers <= rp->max_num_queues(),
"Mismatch between the number of GC workers %u and the maximum number of Reference process queues %u",
no_of_gc_workers, rp->max_num_queues());
// Parallel reference processing
assert(no_of_gc_workers <= rp->max_num_queues(),
"Mismatch between the number of GC workers %u and the maximum number of Reference process queues %u",
no_of_gc_workers, rp->max_num_queues());
G1STWRefProcTaskExecutor par_task_executor(this, per_thread_states, workers(), _task_queues);
stats = rp->process_discovered_references(&is_alive,
&keep_alive,
&drain_queue,
&par_task_executor,
pt);
}
rp->set_active_mt_degree(no_of_gc_workers);
G1STWRefProcProxyTask task(rp->max_num_queues(), *this, *per_thread_states, *_task_queues);
stats = rp->process_discovered_references(task, pt);
_gc_tracer_stw->report_gc_reference_stats(stats);

View File

@ -1458,71 +1458,34 @@ class G1CMDrainMarkingStackClosure : public VoidClosure {
}
};
// Implementation of AbstractRefProcTaskExecutor for parallel
// reference processing at the end of G1 concurrent marking
class G1CMRefProcTaskExecutor : public AbstractRefProcTaskExecutor {
private:
G1CollectedHeap* _g1h;
G1ConcurrentMark* _cm;
WorkGang* _workers;
uint _active_workers;
class G1CMRefProcProxyTask : public RefProcProxyTask {
G1CollectedHeap& _g1h;
G1ConcurrentMark& _cm;
public:
G1CMRefProcTaskExecutor(G1CollectedHeap* g1h,
G1ConcurrentMark* cm,
WorkGang* workers,
uint n_workers) :
_g1h(g1h), _cm(cm),
_workers(workers), _active_workers(n_workers) { }
G1CMRefProcProxyTask(uint max_workers, G1CollectedHeap& g1h, G1ConcurrentMark &cm)
: RefProcProxyTask("G1CMRefProcProxyTask", max_workers),
_g1h(g1h),
_cm(cm) {}
virtual void execute(ProcessTask& task, uint ergo_workers);
};
class G1CMRefProcTaskProxy : public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
ProcessTask& _proc_task;
G1CollectedHeap* _g1h;
G1ConcurrentMark* _cm;
public:
G1CMRefProcTaskProxy(ProcessTask& proc_task,
G1CollectedHeap* g1h,
G1ConcurrentMark* cm) :
AbstractGangTask("Process reference objects in parallel"),
_proc_task(proc_task), _g1h(g1h), _cm(cm) {
ReferenceProcessor* rp = _g1h->ref_processor_cm();
assert(rp->processing_is_mt(), "shouldn't be here otherwise");
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
G1CMIsAliveClosure is_alive(&_g1h);
uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
G1CMKeepAliveAndDrainClosure keep_alive(&_cm, _cm.task(index), _tm == RefProcThreadModel::Single);
G1CMDrainMarkingStackClosure complete_gc(&_cm, _cm.task(index), _tm == RefProcThreadModel::Single);
_rp_task->rp_work(worker_id, &is_alive, &keep_alive, &complete_gc);
}
virtual void work(uint worker_id) {
ResourceMark rm;
G1CMTask* task = _cm->task(worker_id);
G1CMIsAliveClosure g1_is_alive(_g1h);
G1CMKeepAliveAndDrainClosure g1_par_keep_alive(_cm, task, false /* is_serial */);
G1CMDrainMarkingStackClosure g1_par_drain(_cm, task, false /* is_serial */);
_proc_task.work(worker_id, g1_is_alive, g1_par_keep_alive, g1_par_drain);
void prepare_run_task_hook() override {
// We need to reset the concurrency level before each
// proxy task execution, so that the termination protocol
// and overflow handling in G1CMTask::do_marking_step() knows
// how many workers to wait for.
_cm.set_concurrency(_queue_count);
}
};
void G1CMRefProcTaskExecutor::execute(ProcessTask& proc_task, uint ergo_workers) {
assert(_workers != NULL, "Need parallel worker threads.");
assert(_g1h->ref_processor_cm()->processing_is_mt(), "processing is not MT");
assert(_workers->active_workers() >= ergo_workers,
"Ergonomically chosen workers(%u) should be less than or equal to active workers(%u)",
ergo_workers, _workers->active_workers());
G1CMRefProcTaskProxy proc_task_proxy(proc_task, _g1h, _cm);
// We need to reset the concurrency level before each
// proxy task execution, so that the termination protocol
// and overflow handling in G1CMTask::do_marking_step() knows
// how many workers to wait for.
_cm->set_concurrency(ergo_workers);
_workers->run_task(&proc_task_proxy, ergo_workers);
}
void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
ResourceMark rm;
@ -1541,23 +1504,6 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
rp->setup_policy(clear_all_soft_refs);
assert(_global_mark_stack.is_empty(), "mark stack should be empty");
// Instances of the 'Keep Alive' and 'Complete GC' closures used
// in serial reference processing. Note these closures are also
// used for serially processing (by the the current thread) the
// JNI references during parallel reference processing.
//
// These closures do not need to synchronize with the worker
// threads involved in parallel reference processing as these
// instances are executed serially by the current thread (e.g.
// reference processing is not multi-threaded and is thus
// performed by the current thread instead of a gang worker).
//
// The gang tasks involved in parallel reference processing create
// their own instances of these closures, which do their own
// synchronization among themselves.
G1CMKeepAliveAndDrainClosure g1_keep_alive(this, task(0), true /* is_serial */);
G1CMDrainMarkingStackClosure g1_drain_mark_stack(this, task(0), true /* is_serial */);
// We need at least one active thread. If reference processing
// is not multi-threaded we use the current (VMThread) thread,
// otherwise we use the work gang from the G1CollectedHeap and
@ -1576,19 +1522,11 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
rp->set_active_mt_degree(active_workers);
// Parallel processing task executor.
G1CMRefProcTaskExecutor par_task_executor(_g1h, this,
_g1h->workers(), active_workers);
AbstractRefProcTaskExecutor* executor = (rp->processing_is_mt() ? &par_task_executor : NULL);
G1CMRefProcProxyTask task(rp->max_num_queues(), *_g1h, *this);
ReferenceProcessorPhaseTimes pt(_gc_timer_cm, rp->max_num_queues());
// Process the weak references.
const ReferenceProcessorStats& stats =
rp->process_discovered_references(&g1_is_alive,
&g1_keep_alive,
&g1_drain_mark_stack,
executor,
&pt);
const ReferenceProcessorStats& stats = rp->process_discovered_references(task, pt);
_gc_tracer_cm->report_gc_reference_stats(stats);
pt.print_all_references();

View File

@ -278,15 +278,14 @@ public:
// This class manages data structures and methods for doing liveness analysis in
// G1's concurrent cycle.
class G1ConcurrentMark : public CHeapObj<mtGC> {
friend class G1ConcurrentMarkThread;
friend class G1CMRefProcTaskProxy;
friend class G1CMRefProcTaskExecutor;
friend class G1CMKeepAliveAndDrainClosure;
friend class G1CMDrainMarkingStackClosure;
friend class G1CMBitMapClosure;
friend class G1CMConcurrentMarkingTask;
friend class G1CMDrainMarkingStackClosure;
friend class G1CMKeepAliveAndDrainClosure;
friend class G1CMRefProcProxyTask;
friend class G1CMRemarkTask;
friend class G1CMTask;
friend class G1ConcurrentMarkThread;
G1ConcurrentMarkThread* _cm_thread; // The thread doing the work
G1CollectedHeap* _g1h; // The heap

View File

@ -33,7 +33,6 @@
#include "gc/g1/g1FullGCMarker.inline.hpp"
#include "gc/g1/g1FullGCMarkTask.hpp"
#include "gc/g1/g1FullGCPrepareTask.hpp"
#include "gc/g1/g1FullGCReferenceProcessorExecutor.hpp"
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1OopClosures.hpp"
#include "gc/g1/g1Policy.hpp"
@ -242,6 +241,24 @@ void G1FullCollector::before_marking_update_attribute_table(HeapRegion* hr) {
}
}
class G1FullGCRefProcProxyTask : public RefProcProxyTask {
G1FullCollector& _collector;
public:
G1FullGCRefProcProxyTask(G1FullCollector &collector, uint max_workers)
: RefProcProxyTask("G1FullGCRefProcProxyTask", max_workers),
_collector(collector) {}
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
G1IsAliveClosure is_alive(&_collector);
uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
G1FullKeepAliveClosure keep_alive(_collector.marker(index));
G1FollowStackClosure* complete_gc = _collector.marker(index)->stack_closure();
_rp_task->rp_work(worker_id, &is_alive, &keep_alive, complete_gc);
}
};
void G1FullCollector::phase1_mark_live_objects() {
// Recursively traverse all live objects and mark them.
GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer());
@ -253,9 +270,18 @@ void G1FullCollector::phase1_mark_live_objects() {
}
{
// Process references discovered during marking.
G1FullGCReferenceProcessingExecutor reference_processing(this);
reference_processing.execute(scope()->timer(), scope()->tracer());
uint old_active_mt_degree = reference_processor()->num_queues();
reference_processor()->set_active_mt_degree(workers());
GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", scope()->timer());
// Process reference objects found during marking.
ReferenceProcessorPhaseTimes pt(scope()->timer(), reference_processor()->max_num_queues());
G1FullGCRefProcProxyTask task(*this, reference_processor()->max_num_queues());
const ReferenceProcessorStats& stats = reference_processor()->process_discovered_references(task, pt);
scope()->tracer()->report_gc_reference_stats(stats);
pt.print_all_references();
assert(marker(0)->oop_stack()->is_empty(), "Should be no oops on the stack");
reference_processor()->set_active_mt_degree(old_active_mt_degree);
}
// Weak oops cleanup.

View File

@ -29,7 +29,6 @@
#include "gc/g1/g1FullGCMarker.hpp"
#include "gc/g1/g1FullGCMarkTask.hpp"
#include "gc/g1/g1FullGCOopClosures.inline.hpp"
#include "gc/g1/g1FullGCReferenceProcessorExecutor.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "memory/iterator.inline.hpp"

View File

@ -1,98 +0,0 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 "precompiled.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1FullCollector.hpp"
#include "gc/g1/g1FullGCMarker.hpp"
#include "gc/g1/g1FullGCOopClosures.inline.hpp"
#include "gc/g1/g1FullGCReferenceProcessorExecutor.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
#include "memory/iterator.inline.hpp"
G1FullGCReferenceProcessingExecutor::G1FullGCReferenceProcessingExecutor(G1FullCollector* collector) :
_collector(collector),
_reference_processor(collector->reference_processor()),
_old_mt_degree(_reference_processor->num_queues()) {
_reference_processor->set_active_mt_degree(_collector->workers());
}
G1FullGCReferenceProcessingExecutor::~G1FullGCReferenceProcessingExecutor() {
_reference_processor->set_active_mt_degree(_old_mt_degree);
}
G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::G1RefProcTaskProxy(ProcessTask& proc_task,
G1FullCollector* collector) :
AbstractGangTask("G1 reference processing task"),
_proc_task(proc_task),
_collector(collector) { }
void G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::work(uint worker_id) {
G1FullGCMarker* marker = _collector->marker(worker_id);
G1IsAliveClosure is_alive(_collector);
G1FullKeepAliveClosure keep_alive(marker);
_proc_task.work(worker_id,
is_alive,
keep_alive,
*marker->stack_closure());
}
void G1FullGCReferenceProcessingExecutor::run_task(AbstractGangTask* task) {
G1CollectedHeap::heap()->workers()->run_task(task, _collector->workers());
}
void G1FullGCReferenceProcessingExecutor::run_task(AbstractGangTask* task, uint workers) {
G1CollectedHeap::heap()->workers()->run_task(task, workers);
}
void G1FullGCReferenceProcessingExecutor::execute(ProcessTask& proc_task, uint ergo_workers) {
G1RefProcTaskProxy proc_task_proxy(proc_task, _collector);
run_task(&proc_task_proxy, ergo_workers);
}
void G1FullGCReferenceProcessingExecutor::execute(STWGCTimer* timer, G1FullGCTracer* tracer) {
GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", timer);
// Process reference objects found during marking.
G1FullGCMarker* marker = _collector->marker(0);
G1IsAliveClosure is_alive(_collector);
G1FullKeepAliveClosure keep_alive(marker);
ReferenceProcessorPhaseTimes pt(timer, _reference_processor->max_num_queues());
AbstractRefProcTaskExecutor* executor = _reference_processor->processing_is_mt() ? this : NULL;
// Process discovered references, use this executor if multi-threaded
// processing is enabled.
const ReferenceProcessorStats& stats =
_reference_processor->process_discovered_references(&is_alive,
&keep_alive,
marker->stack_closure(),
executor,
&pt);
tracer->report_gc_reference_stats(stats);
pt.print_all_references();
assert(marker->oop_stack()->is_empty(), "Should be no oops on the stack");
}

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*
*/
#ifndef SHARE_GC_G1_G1FULLGCREFERENCEPROCESSOREXECUTOR_HPP
#define SHARE_GC_G1_G1FULLGCREFERENCEPROCESSOREXECUTOR_HPP
#include "gc/g1/g1FullGCCompactionPoint.hpp"
#include "gc/g1/g1FullGCScope.hpp"
#include "gc/g1/g1FullGCTask.hpp"
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/taskqueue.hpp"
#include "utilities/ticks.hpp"
class G1FullGCTracer;
class STWGCTimer;
class G1FullGCReferenceProcessingExecutor: public AbstractRefProcTaskExecutor {
G1FullCollector* _collector;
ReferenceProcessor* _reference_processor;
uint _old_mt_degree;
public:
G1FullGCReferenceProcessingExecutor(G1FullCollector* collector);
~G1FullGCReferenceProcessingExecutor();
// Do reference processing.
void execute(STWGCTimer* timer, G1FullGCTracer* tracer);
// Executes the given task using concurrent marking worker threads.
virtual void execute(ProcessTask& task, uint ergo_workers);
private:
void run_task(AbstractGangTask* task);
void run_task(AbstractGangTask* task, uint workers);
class G1RefProcTaskProxy : public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
ProcessTask& _proc_task;
G1FullCollector* _collector;
public:
G1RefProcTaskProxy(ProcessTask& proc_task,
G1FullCollector* scope);
virtual void work(uint worker_id);
};
};
#endif // SHARE_GC_G1_G1FULLGCREFERENCEPROCESSOREXECUTOR_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,7 @@
#include "gc/parallel/psParallelCompact.hpp"
#include "gc/shared/taskqueue.hpp"
#include "gc/shared/taskTerminator.hpp"
#include "memory/allocation.hpp"
#include "utilities/stack.hpp"
@ -38,14 +39,13 @@ class ParallelCompactData;
class ParMarkBitMap;
class ParCompactionManager : public CHeapObj<mtGC> {
friend class MarkFromRootsTask;
friend class ParallelCompactRefProcProxyTask;
friend class ParallelScavengeRefProcProxyTask;
friend class ParMarkBitMap;
friend class PSParallelCompact;
friend class CompactionWithStealingTask;
friend class UpdateAndFillClosure;
friend class RefProcTaskExecutor;
friend class PCRefProcTask;
friend class MarkFromRootsTask;
friend class UpdateDensePrefixAndCompactionTask;
private:
typedef GenericTaskQueue<oop, mtGC> OopTaskQueue;
typedef GenericTaskQueueSet<OopTaskQueue, mtGC> OopTaskQueueSet;
@ -187,8 +187,11 @@ class ParCompactionManager : public CHeapObj<mtGC> {
class FollowStackClosure: public VoidClosure {
private:
ParCompactionManager* _compaction_manager;
TaskTerminator* _terminator;
uint _worker_id;
public:
FollowStackClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
FollowStackClosure(ParCompactionManager* cm, TaskTerminator* terminator, uint worker_id)
: _compaction_manager(cm), _terminator(terminator), _worker_id(worker_id) { }
virtual void do_void();
};

View File

@ -118,6 +118,9 @@ inline void ParCompactionManager::follow_klass(Klass* klass) {
inline void ParCompactionManager::FollowStackClosure::do_void() {
_compaction_manager->follow_marking_stacks();
if (_terminator != nullptr) {
steal_marking_work(*_terminator, _worker_id);
}
}
template <typename T>

View File

@ -1996,7 +1996,7 @@ static void mark_from_roots_work(ParallelRootType::Value root_type, uint worker_
cm->follow_marking_stacks();
}
static void steal_marking_work(TaskTerminator& terminator, uint worker_id) {
void steal_marking_work(TaskTerminator& terminator, uint worker_id) {
assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc");
ParCompactionManager* cm =
@ -2017,7 +2017,6 @@ static void steal_marking_work(TaskTerminator& terminator, uint worker_id) {
}
class MarkFromRootsTask : public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
StrongRootsScope _strong_roots_scope; // needed for Threads::possibly_parallel_threads_do
OopStorageSetStrongParState<false /* concurrent */, false /* is_const */> _oop_storage_set_par_state;
SequentialSubTasksDone _subtasks;
@ -2056,43 +2055,24 @@ public:
}
};
class PCRefProcTask : public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
ProcessTask& _task;
uint _ergo_workers;
class ParallelCompactRefProcProxyTask : public RefProcProxyTask {
TaskTerminator _terminator;
public:
PCRefProcTask(ProcessTask& task, uint ergo_workers) :
AbstractGangTask("PCRefProcTask"),
_task(task),
_ergo_workers(ergo_workers),
_terminator(_ergo_workers, ParCompactionManager::oop_task_queues()) {
ParallelCompactRefProcProxyTask(uint max_workers)
: RefProcProxyTask("ParallelCompactRefProcProxyTask", max_workers),
_terminator(_max_workers, ParCompactionManager::oop_task_queues()) {}
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
ParCompactionManager* cm = (_tm == RefProcThreadModel::Single) ? ParCompactionManager::get_vmthread_cm() : ParCompactionManager::gc_thread_compaction_manager(worker_id);
PCMarkAndPushClosure keep_alive(cm);
ParCompactionManager::FollowStackClosure complete_gc(cm, (_tm == RefProcThreadModel::Single) ? nullptr : &_terminator, worker_id);
_rp_task->rp_work(worker_id, PSParallelCompact::is_alive_closure(), &keep_alive, &complete_gc);
}
virtual void work(uint worker_id) {
ParallelScavengeHeap* heap = ParallelScavengeHeap::heap();
assert(ParallelScavengeHeap::heap()->is_gc_active(), "called outside gc");
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(worker_id);
PCMarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::FollowStackClosure follow_stack_closure(cm);
_task.work(worker_id, *PSParallelCompact::is_alive_closure(),
mark_and_push_closure, follow_stack_closure);
steal_marking_work(_terminator, worker_id);
}
};
class RefProcTaskExecutor: public AbstractRefProcTaskExecutor {
void execute(ProcessTask& process_task, uint ergo_workers) {
assert(ParallelScavengeHeap::heap()->workers().active_workers() == ergo_workers,
"Ergonomically chosen workers (%u) must be equal to active workers (%u)",
ergo_workers, ParallelScavengeHeap::heap()->workers().active_workers());
PCRefProcTask task(process_task, ergo_workers);
ParallelScavengeHeap::heap()->workers().run_task(&task);
void prepare_run_task_hook() override {
_terminator.reset_for_reuse(_queue_count);
}
};
@ -2102,12 +2082,8 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm,
// Recursively traverse all live objects and mark them
GCTraceTime(Info, gc, phases) tm("Marking Phase", &_gc_timer);
ParallelScavengeHeap* heap = ParallelScavengeHeap::heap();
uint active_gc_threads = ParallelScavengeHeap::heap()->workers().active_workers();
PCMarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::FollowStackClosure follow_stack_closure(cm);
// Need new claim bits before marking starts.
ClassLoaderDataGraph::clear_claimed_marks();
@ -2126,16 +2102,8 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm,
ReferenceProcessorPhaseTimes pt(&_gc_timer, ref_processor()->max_num_queues());
ref_processor()->set_active_mt_degree(active_gc_threads);
if (ref_processor()->processing_is_mt()) {
RefProcTaskExecutor task_executor;
stats = ref_processor()->process_discovered_references(
is_alive_closure(), &mark_and_push_closure, &follow_stack_closure,
&task_executor, &pt);
} else {
stats = ref_processor()->process_discovered_references(
is_alive_closure(), &mark_and_push_closure, &follow_stack_closure, NULL,
&pt);
}
ParallelCompactRefProcProxyTask task(ref_processor()->max_num_queues());
stats = ref_processor()->process_discovered_references(task, pt);
gc_tracer->report_gc_reference_stats(stats);
pt.print_all_references();
@ -2522,7 +2490,6 @@ static void compaction_with_stealing_work(TaskTerminator* terminator, uint worke
}
class UpdateDensePrefixAndCompactionTask: public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
TaskQueue& _tq;
TaskTerminator _terminator;
uint _active_workers;

View File

@ -27,10 +27,11 @@
#include "gc/parallel/mutableSpace.hpp"
#include "gc/parallel/objectStartArray.hpp"
#include "gc/parallel/parMarkBitMap.hpp"
#include "gc/parallel/parallelScavengeHeap.hpp"
#include "gc/parallel/parMarkBitMap.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/collectorCounters.hpp"
#include "gc/shared/taskTerminator.hpp"
#include "oops/oop.hpp"
#include "runtime/atomic.hpp"
#include "runtime/orderAccess.hpp"
@ -1390,4 +1391,6 @@ class FillClosure: public ParMarkBitMapClosure {
ObjectStartArray* const _start_array;
};
void steal_marking_work(TaskTerminator& terminator, uint worker_id);
#endif // SHARE_GC_PARALLEL_PSPARALLELCOMPACT_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -52,8 +52,6 @@ class ParCompactionManager;
class PSPromotionManager {
friend class PSScavenge;
friend class ScavengeRootsTask;
friend class PSRefProcTaskExecutor;
friend class PSRefProcTask;
private:
typedef OverflowTaskQueue<ScannerTask, mtGC> PSScannerTasksQueue;

View File

@ -179,54 +179,46 @@ public:
class PSEvacuateFollowersClosure: public VoidClosure {
private:
PSPromotionManager* _promotion_manager;
TaskTerminator* _terminator;
uint _worker_id;
public:
PSEvacuateFollowersClosure(PSPromotionManager* pm) : _promotion_manager(pm) {}
PSEvacuateFollowersClosure(PSPromotionManager* pm, TaskTerminator* terminator, uint worker_id)
: _promotion_manager(pm), _terminator(terminator), _worker_id(worker_id) {}
virtual void do_void() {
assert(_promotion_manager != NULL, "Sanity");
assert(_promotion_manager != nullptr, "Sanity");
_promotion_manager->drain_stacks(true);
guarantee(_promotion_manager->stacks_empty(),
"stacks should be empty at this point");
}
};
class PSRefProcTaskExecutor: public AbstractRefProcTaskExecutor {
virtual void execute(ProcessTask& process_task, uint ergo_workers);
};
class PSRefProcTask : public AbstractGangTask {
typedef AbstractRefProcTaskExecutor::ProcessTask ProcessTask;
TaskTerminator _terminator;
ProcessTask& _task;
uint _active_workers;
public:
PSRefProcTask(ProcessTask& task, uint active_workers)
: AbstractGangTask("PSRefProcTask"),
_terminator(active_workers, PSPromotionManager::stack_array_depth()),
_task(task),
_active_workers(active_workers) {
}
virtual void work(uint worker_id) {
PSPromotionManager* promotion_manager =
PSPromotionManager::gc_thread_promotion_manager(worker_id);
assert(promotion_manager != NULL, "sanity check");
PSKeepAliveClosure keep_alive(promotion_manager);
PSEvacuateFollowersClosure evac_followers(promotion_manager);
PSIsAliveClosure is_alive;
_task.work(worker_id, is_alive, keep_alive, evac_followers);
if (_task.marks_oops_alive() && _active_workers > 1) {
steal_work(_terminator, worker_id);
if (_terminator != nullptr) {
steal_work(*_terminator, _worker_id);
}
}
};
void PSRefProcTaskExecutor::execute(ProcessTask& process_task, uint ergo_workers) {
PSRefProcTask task(process_task, ergo_workers);
ParallelScavengeHeap::heap()->workers().run_task(&task);
}
class ParallelScavengeRefProcProxyTask : public RefProcProxyTask {
TaskTerminator _terminator;
public:
ParallelScavengeRefProcProxyTask(uint max_workers)
: RefProcProxyTask("ParallelScavengeRefProcProxyTask", max_workers),
_terminator(max_workers, ParCompactionManager::oop_task_queues()) {}
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
PSPromotionManager* promotion_manager = (_tm == RefProcThreadModel::Single) ? PSPromotionManager::vm_thread_promotion_manager() : PSPromotionManager::gc_thread_promotion_manager(worker_id);
PSIsAliveClosure is_alive;
PSKeepAliveClosure keep_alive(promotion_manager);;
PSEvacuateFollowersClosure complete_gc(promotion_manager, (_marks_oops_alive && _tm == RefProcThreadModel::Multi) ? &_terminator : nullptr, worker_id);;
_rp_task->rp_work(worker_id, &is_alive, &keep_alive, &complete_gc);
}
void prepare_run_task_hook() override {
_terminator.reset_for_reuse(_queue_count);
}
};
// This method contains all heap specific policy for invoking scavenge.
// PSScavenge::invoke_no_policy() will do nothing but attempt to
@ -496,19 +488,11 @@ bool PSScavenge::invoke_no_policy() {
reference_processor()->setup_policy(false); // not always_clear
reference_processor()->set_active_mt_degree(active_workers);
PSKeepAliveClosure keep_alive(promotion_manager);
PSEvacuateFollowersClosure evac_followers(promotion_manager);
ReferenceProcessorStats stats;
ReferenceProcessorPhaseTimes pt(&_gc_timer, reference_processor()->max_num_queues());
if (reference_processor()->processing_is_mt()) {
PSRefProcTaskExecutor task_executor;
stats = reference_processor()->process_discovered_references(
&_is_alive_closure, &keep_alive, &evac_followers, &task_executor,
&pt);
} else {
stats = reference_processor()->process_discovered_references(
&_is_alive_closure, &keep_alive, &evac_followers, NULL, &pt);
}
ParallelScavengeRefProcProxyTask task(reference_processor()->max_num_queues());
stats = reference_processor()->process_discovered_references(task, pt);
_gc_tracer.report_gc_reference_stats(stats);
pt.print_all_references();

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc/serial/defNewGeneration.inline.hpp"
#include "gc/serial/serialGcRefProcProxyTask.hpp"
#include "gc/serial/serialHeap.inline.hpp"
#include "gc/serial/tenuredGeneration.hpp"
#include "gc/shared/adaptiveSizePolicy.hpp"
@ -37,8 +38,8 @@
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTrace.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/genOopClosures.inline.hpp"
#include "gc/shared/generationSpec.hpp"
#include "gc/shared/genOopClosures.inline.hpp"
#include "gc/shared/preservedMarks.inline.hpp"
#include "gc/shared/referencePolicy.hpp"
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
@ -587,9 +588,8 @@ void DefNewGeneration::collect(bool full,
ReferenceProcessor* rp = ref_processor();
rp->setup_policy(clear_all_soft_refs);
ReferenceProcessorPhaseTimes pt(_gc_timer, rp->max_num_queues());
const ReferenceProcessorStats& stats =
rp->process_discovered_references(&is_alive, &keep_alive, &evacuate_followers,
NULL, &pt);
SerialGCRefProcProxyTask task(is_alive, keep_alive, evacuate_followers);
const ReferenceProcessorStats& stats = rp->process_discovered_references(task, pt);
gc_tracer.report_gc_reference_stats(stats);
gc_tracer.report_tenuring_threshold(tenuring_threshold());
pt.print_all_references();

View File

@ -33,6 +33,7 @@
#include "code/icBuffer.hpp"
#include "compiler/oopMap.hpp"
#include "gc/serial/genMarkSweep.hpp"
#include "gc/serial/serialGcRefProcProxyTask.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "gc/shared/gcHeapSummary.hpp"
#include "gc/shared/gcTimer.hpp"
@ -199,9 +200,8 @@ void GenMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) {
ref_processor()->setup_policy(clear_all_softrefs);
ReferenceProcessorPhaseTimes pt(_gc_timer, ref_processor()->max_num_queues());
const ReferenceProcessorStats& stats =
ref_processor()->process_discovered_references(
&is_alive, &keep_alive, &follow_stack_closure, NULL, &pt);
SerialGCRefProcProxyTask task(is_alive, keep_alive, follow_stack_closure);
const ReferenceProcessorStats& stats = ref_processor()->process_discovered_references(task, pt);
pt.print_all_references();
gc_tracer()->report_gc_reference_stats(stats);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*
*/
#ifndef SHARE_GC_SERIAL_SERIALGCREFPROCPROXYTASK_HPP
#define SHARE_GC_SERIAL_SERIALGCREFPROCPROXYTASK_HPP
#include "gc/shared/referenceProcessor.hpp"
class SerialGCRefProcProxyTask : public RefProcProxyTask {
BoolObjectClosure& _is_alive;
OopClosure& _keep_alive;
VoidClosure& _complete_gc;
public:
SerialGCRefProcProxyTask(BoolObjectClosure& is_alive, OopClosure& keep_alive, VoidClosure& complete_gc)
: RefProcProxyTask("SerialGCRefProcProxyTask", 1),
_is_alive(is_alive),
_keep_alive(keep_alive),
_complete_gc(complete_gc) {}
void work(uint worker_id) override {
assert(worker_id < _max_workers, "sanity");
_rp_task->rp_work(worker_id, &_is_alive, &_keep_alive, &_complete_gc);
}
};
#endif /* SHARE_GC_SERIAL_SERIALGCREFPROCPROXYTASK_HPP */

View File

@ -196,12 +196,8 @@ void ReferenceProcessor::verify_total_count_zero(DiscoveredList lists[], const c
}
#endif
ReferenceProcessorStats ReferenceProcessor::process_discovered_references(
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times) {
ReferenceProcessorStats ReferenceProcessor::process_discovered_references(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times) {
double start_time = os::elapsedTime();
@ -225,29 +221,28 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references(
total_count(_discoveredPhantomRefs));
{
RefProcTotalPhaseTimesTracker tt(RefPhase1, phase_times, this);
process_soft_ref_reconsider(is_alive, keep_alive, complete_gc,
task_executor, phase_times);
RefProcTotalPhaseTimesTracker tt(RefPhase1, &phase_times);
process_soft_ref_reconsider(proxy_task, phase_times);
}
update_soft_ref_master_clock();
{
RefProcTotalPhaseTimesTracker tt(RefPhase2, phase_times, this);
process_soft_weak_final_refs(is_alive, keep_alive, complete_gc, task_executor, phase_times);
RefProcTotalPhaseTimesTracker tt(RefPhase2, &phase_times);
process_soft_weak_final_refs(proxy_task, phase_times);
}
{
RefProcTotalPhaseTimesTracker tt(RefPhase3, phase_times, this);
process_final_keep_alive(keep_alive, complete_gc, task_executor, phase_times);
RefProcTotalPhaseTimesTracker tt(RefPhase3, &phase_times);
process_final_keep_alive(proxy_task, phase_times);
}
{
RefProcTotalPhaseTimesTracker tt(RefPhase4, phase_times, this);
process_phantom_refs(is_alive, keep_alive, complete_gc, task_executor, phase_times);
RefProcTotalPhaseTimesTracker tt(RefPhase4, &phase_times);
process_phantom_refs(proxy_task, phase_times);
}
phase_times->set_total_time_ms((os::elapsedTime() - start_time) * 1000);
phase_times.set_total_time_ms((os::elapsedTime() - start_time) * 1000);
return stats;
}
@ -441,9 +436,9 @@ size_t ReferenceProcessor::process_final_keep_alive_work(DiscoveredList& refs_li
}
size_t ReferenceProcessor::process_phantom_refs_work(DiscoveredList& refs_list,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) {
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) {
DiscoveredListIterator iter(refs_list, keep_alive, is_alive);
while (iter.has_next()) {
iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */));
@ -516,41 +511,45 @@ size_t ReferenceProcessor::total_reference_count(ReferenceType type) const {
return total_count(list);
}
class RefProcPhase1Task : public AbstractRefProcTaskExecutor::ProcessTask {
class RefProcPhase1Task : public RefProcTask {
public:
RefProcPhase1Task(ReferenceProcessor& ref_processor,
RefProcPhase1Task(ReferenceProcessor& ref_processor,
ReferenceProcessorPhaseTimes* phase_times,
ReferencePolicy* policy)
: ProcessTask(ref_processor, true /* marks_oops_alive */, phase_times),
ReferencePolicy* policy)
: RefProcTask(ref_processor,
phase_times),
_policy(policy) { }
virtual void work(uint worker_id,
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
VoidClosure& complete_gc)
{
void rp_work(uint worker_id,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) override {
ResourceMark rm;
RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::SoftRefSubPhase1, _phase_times, worker_id);
size_t const removed = _ref_processor.process_soft_ref_reconsider_work(_ref_processor._discoveredSoftRefs[worker_id],
_policy,
&is_alive,
&keep_alive,
&complete_gc);
is_alive,
keep_alive,
complete_gc);
_phase_times->add_ref_cleared(REF_SOFT, removed);
}
private:
ReferencePolicy* _policy;
};
class RefProcPhase2Task: public AbstractRefProcTaskExecutor::ProcessTask {
class RefProcPhase2Task: public RefProcTask {
void run_phase2(uint worker_id,
DiscoveredList list[],
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
bool do_enqueue_and_clear,
ReferenceType ref_type) {
size_t const removed = _ref_processor.process_soft_weak_final_refs_work(list[worker_id],
&is_alive,
&keep_alive,
is_alive,
keep_alive,
do_enqueue_and_clear);
_phase_times->add_ref_cleared(ref_type, removed);
}
@ -558,12 +557,14 @@ class RefProcPhase2Task: public AbstractRefProcTaskExecutor::ProcessTask {
public:
RefProcPhase2Task(ReferenceProcessor& ref_processor,
ReferenceProcessorPhaseTimes* phase_times)
: ProcessTask(ref_processor, false /* marks_oops_alive */, phase_times) { }
: RefProcTask(ref_processor,
phase_times) {}
virtual void work(uint worker_id,
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
VoidClosure& complete_gc) {
void rp_work(uint worker_id,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) override {
ResourceMark rm;
RefProcWorkerTimeTracker t(_phase_times->phase2_worker_time_sec(), worker_id);
{
RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::SoftRefSubPhase2, _phase_times, worker_id);
@ -579,42 +580,44 @@ public:
}
// Close the reachable set; needed for collectors which keep_alive_closure do
// not immediately complete their work.
complete_gc.do_void();
complete_gc->do_void();
}
};
class RefProcPhase3Task: public AbstractRefProcTaskExecutor::ProcessTask {
class RefProcPhase3Task: public RefProcTask {
public:
RefProcPhase3Task(ReferenceProcessor& ref_processor,
RefProcPhase3Task(ReferenceProcessor& ref_processor,
ReferenceProcessorPhaseTimes* phase_times)
: ProcessTask(ref_processor, true /* marks_oops_alive */, phase_times) { }
: RefProcTask(ref_processor,
phase_times) {}
virtual void work(uint worker_id,
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
VoidClosure& complete_gc)
{
void rp_work(uint worker_id,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) override {
ResourceMark rm;
RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::FinalRefSubPhase3, _phase_times, worker_id);
_ref_processor.process_final_keep_alive_work(_ref_processor._discoveredFinalRefs[worker_id], &keep_alive, &complete_gc);
_ref_processor.process_final_keep_alive_work(_ref_processor._discoveredFinalRefs[worker_id], keep_alive, complete_gc);
}
};
class RefProcPhase4Task: public AbstractRefProcTaskExecutor::ProcessTask {
class RefProcPhase4Task: public RefProcTask {
public:
RefProcPhase4Task(ReferenceProcessor& ref_processor,
RefProcPhase4Task(ReferenceProcessor& ref_processor,
ReferenceProcessorPhaseTimes* phase_times)
: ProcessTask(ref_processor, false /* marks_oops_alive */, phase_times) { }
: RefProcTask(ref_processor,
phase_times) {}
virtual void work(uint worker_id,
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
VoidClosure& complete_gc)
{
void rp_work(uint worker_id,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) override {
ResourceMark rm;
RefProcSubPhasesWorkerTimeTracker tt(ReferenceProcessor::PhantomRefSubPhase4, _phase_times, worker_id);
size_t const removed = _ref_processor.process_phantom_refs_work(_ref_processor._discoveredPhantomRefs[worker_id],
&is_alive,
&keep_alive,
&complete_gc);
is_alive,
keep_alive,
complete_gc);
_phase_times->add_ref_cleared(REF_PHANTOM, removed);
}
};
@ -771,16 +774,33 @@ void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[])
#endif
}
void ReferenceProcessor::process_soft_ref_reconsider(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times) {
assert(!processing_is_mt() || task_executor != NULL, "Task executor must not be NULL when mt processing is set.");
void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_task, bool marks_oops_alive) {
log_debug(gc, ref)("ReferenceProcessor::execute queues: %d, %s, marks_oops_alive: %s",
num_queues(),
processing_is_mt() ? "RefProcThreadModel::Multi" : "RefProcThreadModel::Single",
marks_oops_alive ? "true" : "false");
proxy_task.prepare_run_task(task, num_queues(), processing_is_mt() ? RefProcThreadModel::Multi : RefProcThreadModel::Single, marks_oops_alive);
if (processing_is_mt()) {
WorkGang* gang = Universe::heap()->safepoint_workers();
assert(gang != NULL, "can not dispatch multi threaded without a work gang");
assert(gang->active_workers() >= num_queues(),
"Ergonomically chosen workers(%u) should be less than or equal to active workers(%u)",
num_queues(), gang->active_workers());
gang->run_task(&proxy_task, num_queues());
} else {
for (unsigned i = 0; i < _max_num_queues; ++i) {
proxy_task.work(i);
}
}
}
void ReferenceProcessor::process_soft_ref_reconsider(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times) {
size_t const num_soft_refs = total_count(_discoveredSoftRefs);
phase_times->set_ref_discovered(REF_SOFT, num_soft_refs);
phase_times->set_processing_is_mt(processing_is_mt());
phase_times.set_ref_discovered(REF_SOFT, num_soft_refs);
phase_times.set_processing_is_mt(processing_is_mt());
if (num_soft_refs == 0) {
log_debug(gc, ref)("Skipped phase 1 of Reference Processing: no references");
@ -795,45 +815,29 @@ void ReferenceProcessor::process_soft_ref_reconsider(BoolObjectClosure* is_alive
RefProcMTDegreeAdjuster a(this, RefPhase1, num_soft_refs);
if (processing_is_mt()) {
RefProcBalanceQueuesTimeTracker tt(RefPhase1, phase_times);
RefProcBalanceQueuesTimeTracker tt(RefPhase1, &phase_times);
maybe_balance_queues(_discoveredSoftRefs);
}
RefProcPhaseTimeTracker tt(RefPhase1, phase_times);
RefProcPhaseTimeTracker tt(RefPhase1, &phase_times);
log_reflist("Phase 1 Soft before", _discoveredSoftRefs, _max_num_queues);
if (processing_is_mt()) {
RefProcPhase1Task phase1(*this, phase_times, _current_soft_ref_policy);
task_executor->execute(phase1, num_queues());
} else {
size_t removed = 0;
RefProcSubPhasesWorkerTimeTracker tt2(SoftRefSubPhase1, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
removed += process_soft_ref_reconsider_work(_discoveredSoftRefs[i], _current_soft_ref_policy,
is_alive, keep_alive, complete_gc);
}
phase_times->add_ref_cleared(REF_SOFT, removed);
}
RefProcPhase1Task phase1(*this, &phase_times, _current_soft_ref_policy);
run_task(phase1, proxy_task, true);
log_reflist("Phase 1 Soft after", _discoveredSoftRefs, _max_num_queues);
}
void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times) {
assert(!processing_is_mt() || task_executor != NULL, "Task executor must not be NULL when mt processing is set.");
void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times) {
size_t const num_soft_refs = total_count(_discoveredSoftRefs);
size_t const num_weak_refs = total_count(_discoveredWeakRefs);
size_t const num_final_refs = total_count(_discoveredFinalRefs);
size_t const num_total_refs = num_soft_refs + num_weak_refs + num_final_refs;
phase_times->set_ref_discovered(REF_WEAK, num_weak_refs);
phase_times->set_ref_discovered(REF_FINAL, num_final_refs);
phase_times.set_ref_discovered(REF_WEAK, num_weak_refs);
phase_times.set_ref_discovered(REF_FINAL, num_final_refs);
phase_times->set_processing_is_mt(processing_is_mt());
phase_times.set_processing_is_mt(processing_is_mt());
if (num_total_refs == 0) {
log_debug(gc, ref)("Skipped phase 2 of Reference Processing: no references");
@ -843,68 +847,31 @@ void ReferenceProcessor::process_soft_weak_final_refs(BoolObjectClosure* is_aliv
RefProcMTDegreeAdjuster a(this, RefPhase2, num_total_refs);
if (processing_is_mt()) {
RefProcBalanceQueuesTimeTracker tt(RefPhase2, phase_times);
RefProcBalanceQueuesTimeTracker tt(RefPhase2, &phase_times);
maybe_balance_queues(_discoveredSoftRefs);
maybe_balance_queues(_discoveredWeakRefs);
maybe_balance_queues(_discoveredFinalRefs);
}
RefProcPhaseTimeTracker tt(RefPhase2, phase_times);
RefProcPhaseTimeTracker tt(RefPhase2, &phase_times);
log_reflist("Phase 2 Soft before", _discoveredSoftRefs, _max_num_queues);
log_reflist("Phase 2 Weak before", _discoveredWeakRefs, _max_num_queues);
log_reflist("Phase 2 Final before", _discoveredFinalRefs, _max_num_queues);
if (processing_is_mt()) {
RefProcPhase2Task phase2(*this, phase_times);
task_executor->execute(phase2, num_queues());
} else {
RefProcWorkerTimeTracker t(phase_times->phase2_worker_time_sec(), 0);
{
size_t removed = 0;
RefProcSubPhasesWorkerTimeTracker tt2(SoftRefSubPhase2, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
removed += process_soft_weak_final_refs_work(_discoveredSoftRefs[i], is_alive, keep_alive, true /* do_enqueue */);
}
RefProcPhase2Task phase2(*this, &phase_times);
run_task(phase2, proxy_task, false);
phase_times->add_ref_cleared(REF_SOFT, removed);
}
{
size_t removed = 0;
RefProcSubPhasesWorkerTimeTracker tt2(WeakRefSubPhase2, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
removed += process_soft_weak_final_refs_work(_discoveredWeakRefs[i], is_alive, keep_alive, true /* do_enqueue */);
}
phase_times->add_ref_cleared(REF_WEAK, removed);
}
{
size_t removed = 0;
RefProcSubPhasesWorkerTimeTracker tt2(FinalRefSubPhase2, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
removed += process_soft_weak_final_refs_work(_discoveredFinalRefs[i], is_alive, keep_alive, false /* do_enqueue */);
}
phase_times->add_ref_cleared(REF_FINAL, removed);
}
complete_gc->do_void();
}
verify_total_count_zero(_discoveredSoftRefs, "SoftReference");
verify_total_count_zero(_discoveredWeakRefs, "WeakReference");
log_reflist("Phase 2 Final after", _discoveredFinalRefs, _max_num_queues);
}
void ReferenceProcessor::process_final_keep_alive(OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times) {
assert(!processing_is_mt() || task_executor != NULL, "Task executor must not be NULL when mt processing is set.");
void ReferenceProcessor::process_final_keep_alive(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times) {
size_t const num_final_refs = total_count(_discoveredFinalRefs);
phase_times->set_processing_is_mt(processing_is_mt());
phase_times.set_processing_is_mt(processing_is_mt());
if (num_final_refs == 0) {
log_debug(gc, ref)("Skipped phase 3 of Reference Processing: no references");
@ -914,37 +881,25 @@ void ReferenceProcessor::process_final_keep_alive(OopClosure* keep_alive,
RefProcMTDegreeAdjuster a(this, RefPhase3, num_final_refs);
if (processing_is_mt()) {
RefProcBalanceQueuesTimeTracker tt(RefPhase3, phase_times);
RefProcBalanceQueuesTimeTracker tt(RefPhase3, &phase_times);
maybe_balance_queues(_discoveredFinalRefs);
}
// Phase 3:
// . Traverse referents of final references and keep them and followers alive.
RefProcPhaseTimeTracker tt(RefPhase3, phase_times);
RefProcPhaseTimeTracker tt(RefPhase3, &phase_times);
RefProcPhase3Task phase3(*this, &phase_times);
run_task(phase3, proxy_task, true);
if (processing_is_mt()) {
RefProcPhase3Task phase3(*this, phase_times);
task_executor->execute(phase3, num_queues());
} else {
RefProcSubPhasesWorkerTimeTracker tt2(FinalRefSubPhase3, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
process_final_keep_alive_work(_discoveredFinalRefs[i], keep_alive, complete_gc);
}
}
verify_total_count_zero(_discoveredFinalRefs, "FinalReference");
}
void ReferenceProcessor::process_phantom_refs(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times) {
assert(!processing_is_mt() || task_executor != NULL, "Task executor must not be NULL when mt processing is set.");
void ReferenceProcessor::process_phantom_refs(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times) {
size_t const num_phantom_refs = total_count(_discoveredPhantomRefs);
phase_times->set_ref_discovered(REF_PHANTOM, num_phantom_refs);
phase_times->set_processing_is_mt(processing_is_mt());
phase_times.set_ref_discovered(REF_PHANTOM, num_phantom_refs);
phase_times.set_processing_is_mt(processing_is_mt());
if (num_phantom_refs == 0) {
log_debug(gc, ref)("Skipped phase 4 of Reference Processing: no references");
@ -954,27 +909,18 @@ void ReferenceProcessor::process_phantom_refs(BoolObjectClosure* is_alive,
RefProcMTDegreeAdjuster a(this, RefPhase4, num_phantom_refs);
if (processing_is_mt()) {
RefProcBalanceQueuesTimeTracker tt(RefPhase4, phase_times);
RefProcBalanceQueuesTimeTracker tt(RefPhase4, &phase_times);
maybe_balance_queues(_discoveredPhantomRefs);
}
// Phase 4: Walk phantom references appropriately.
RefProcPhaseTimeTracker tt(RefPhase4, phase_times);
RefProcPhaseTimeTracker tt(RefPhase4, &phase_times);
log_reflist("Phase 4 Phantom before", _discoveredPhantomRefs, _max_num_queues);
if (processing_is_mt()) {
RefProcPhase4Task phase4(*this, phase_times);
task_executor->execute(phase4, num_queues());
} else {
size_t removed = 0;
RefProcSubPhasesWorkerTimeTracker tt(PhantomRefSubPhase4, phase_times, 0);
for (uint i = 0; i < _max_num_queues; i++) {
removed += process_phantom_refs_work(_discoveredPhantomRefs[i], is_alive, keep_alive, complete_gc);
}
RefProcPhase4Task phase4(*this, &phase_times);
run_task(phase4, proxy_task, false);
phase_times->add_ref_cleared(REF_PHANTOM, removed);
}
verify_total_count_zero(_discoveredPhantomRefs, "PhantomReference");
}

View File

@ -28,13 +28,15 @@
#include "gc/shared/referenceDiscoverer.hpp"
#include "gc/shared/referencePolicy.hpp"
#include "gc/shared/referenceProcessorStats.hpp"
#include "gc/shared/workgroup.hpp"
#include "memory/referenceType.hpp"
#include "oops/instanceRefKlass.hpp"
class AbstractRefProcTaskExecutor;
class GCTimer;
class ReferencePolicy;
class ReferenceProcessorPhaseTimes;
class RefProcTask;
class RefProcProxyTask;
// List of discovered references.
class DiscoveredList {
@ -235,33 +237,24 @@ private:
DiscoveredList* _discoveredFinalRefs;
DiscoveredList* _discoveredPhantomRefs;
void run_task(RefProcTask& task, RefProcProxyTask& proxy_task, bool marks_oops_alive);
// Phase 1: Re-evaluate soft ref policy.
void process_soft_ref_reconsider(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times);
void process_soft_ref_reconsider(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times);
// Phase 2: Drop Soft/Weak/Final references with a NULL or live referent, and clear
// and enqueue non-Final references.
void process_soft_weak_final_refs(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times);
void process_soft_weak_final_refs(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times);
// Phase 3: Keep alive followers of Final references, and enqueue.
void process_final_keep_alive(OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times);
void process_final_keep_alive(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times);
// Phase 4: Drop and keep alive live Phantom references, or clear and enqueue if dead.
void process_phantom_refs(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times);
void process_phantom_refs(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times);
// Work methods used by the process_* methods. All methods return the number of
// removed elements.
@ -432,11 +425,8 @@ public:
// Process references found during GC (called by the garbage collector)
ReferenceProcessorStats
process_discovered_references(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc,
AbstractRefProcTaskExecutor* task_executor,
ReferenceProcessorPhaseTimes* phase_times);
process_discovered_references(RefProcProxyTask& proxy_task,
ReferenceProcessorPhaseTimes& phase_times);
// If a discovery is in process that is being superceded, abandon it: all
// the discovered lists will be empty, and all the objects on them will
@ -596,42 +586,57 @@ class ReferenceProcessorAtomicMutator: StackObj {
}
};
// This class is an interface used to implement task execution for the
// reference processing.
class AbstractRefProcTaskExecutor {
public:
enum class RefProcThreadModel { Multi, Single };
// Abstract tasks to execute.
class ProcessTask;
// Executes a task using worker threads.
virtual void execute(ProcessTask& task, uint ergo_workers) = 0;
};
// Abstract reference processing task to execute.
class AbstractRefProcTaskExecutor::ProcessTask {
/*
* This is the (base) task that handles reference processing that does not depend on
* the chosen GC (Serial, Parallel or G1). This RefProcTask will be called from a subclass
* of RefProcProxyTask. The RefProcProxyTask will give the behaviour of the selected GC by
* calling rp_work with the gc-specific closures.
*/
class RefProcTask : StackObj {
protected:
ReferenceProcessor& _ref_processor;
// Indicates whether the phase could generate work that should be balanced across
// threads after execution.
bool _marks_oops_alive;
ReferenceProcessor& _ref_processor;
ReferenceProcessorPhaseTimes* _phase_times;
ProcessTask(ReferenceProcessor& ref_processor,
bool marks_oops_alive,
public:
RefProcTask(ReferenceProcessor& ref_processor,
ReferenceProcessorPhaseTimes* phase_times)
: _ref_processor(ref_processor),
_marks_oops_alive(marks_oops_alive),
_phase_times(phase_times)
{ }
_phase_times(phase_times) {}
virtual void rp_work(uint worker_id,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) = 0;
};
/*
* This is the (base) task that handles reference processing that do depend on
* the chosen GC (Serial, Parallel or G1). This RefProcProxyTask will call a subclass
* of RefProcTask that will handle reference processing in a generic way for Serial,
* Parallel and G1. This proxy will add the relevant closures, task terminators etc.
*/
class RefProcProxyTask : public AbstractGangTask {
protected:
const uint _max_workers;
RefProcTask* _rp_task;
RefProcThreadModel _tm;
uint _queue_count;
bool _marks_oops_alive;
public:
virtual void work(uint worker_id,
BoolObjectClosure& is_alive,
OopClosure& keep_alive,
VoidClosure& complete_gc) = 0;
RefProcProxyTask(const char* name, uint max_workers) : AbstractGangTask(name), _max_workers(max_workers), _rp_task(nullptr),_tm(RefProcThreadModel::Single), _queue_count(0), _marks_oops_alive(false) {}
bool marks_oops_alive() const { return _marks_oops_alive; }
void prepare_run_task(RefProcTask& rp_task, uint queue_count, RefProcThreadModel tm, bool marks_oops_alive) {
_rp_task = &rp_task;
_tm = tm;
_queue_count = queue_count;
_marks_oops_alive = marks_oops_alive;
prepare_run_task_hook();
}
virtual void prepare_run_task_hook() {}
};
// Temporarily change the number of workers based on given reference count.

View File

@ -164,9 +164,8 @@ RefProcPhaseTimeTracker::~RefProcPhaseTimeTracker() {
}
RefProcTotalPhaseTimesTracker::RefProcTotalPhaseTimesTracker(ReferenceProcessor::RefProcPhases phase_number,
ReferenceProcessorPhaseTimes* phase_times,
ReferenceProcessor* rp) :
RefProcPhaseTimeBaseTracker(phase_enum_2_phase_string(phase_number), phase_number, phase_times), _rp(rp) {
ReferenceProcessorPhaseTimes* phase_times) :
RefProcPhaseTimeBaseTracker(phase_enum_2_phase_string(phase_number), phase_number, phase_times) {
}
RefProcTotalPhaseTimesTracker::~RefProcTotalPhaseTimesTracker() {

View File

@ -160,11 +160,9 @@ public:
// Highest level time tracker.
class RefProcTotalPhaseTimesTracker : public RefProcPhaseTimeBaseTracker {
ReferenceProcessor* _rp;
public:
RefProcTotalPhaseTimesTracker(ReferenceProcessor::RefProcPhases phase_number,
ReferenceProcessorPhaseTimes* phase_times,
ReferenceProcessor* rp);
ReferenceProcessorPhaseTimes* phase_times);
~RefProcTotalPhaseTimesTracker();
};