8072498: Multi-thread JNI weak reference processing

Add parallel processing support to WeakProcessor.

Reviewed-by: tschatzl, sjohanss
This commit is contained in:
Kim Barrett 2018-08-28 12:57:40 -04:00
parent 49b859b9d3
commit 1b02e70184
17 changed files with 863 additions and 58 deletions

View File

@ -75,7 +75,7 @@
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/referenceProcessor.inline.hpp"
#include "gc/shared/taskqueue.inline.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "memory/iterator.hpp"
@ -3719,14 +3719,8 @@ void G1CollectedHeap::post_evacuate_collection_set(EvacuationInfo& evacuation_in
G1STWIsAliveClosure is_alive(this);
G1KeepAliveClosure keep_alive(this);
{
double start = os::elapsedTime();
WeakProcessor::weak_oops_do(&is_alive, &keep_alive);
double time_ms = (os::elapsedTime() - start) * 1000.0;
g1_policy()->phase_times()->record_weak_ref_proc_time(time_ms);
}
WeakProcessor::weak_oops_do(workers(), &is_alive, &keep_alive,
g1_policy()->phase_times()->weak_phase_times());
if (G1StringDedup::is_enabled()) {
double fixup_start = os::elapsedTime();

View File

@ -50,7 +50,7 @@
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/taskqueue.inline.hpp"
#include "gc/shared/vmGCOperations.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "include/jvm.h"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
@ -1669,7 +1669,7 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
{
GCTraceTime(Debug, gc, phases) debug("Weak Processing", _gc_timer_cm);
WeakProcessor::weak_oops_do(&g1_is_alive, &do_nothing_cl);
WeakProcessor::weak_oops_do(_g1h->workers(), &g1_is_alive, &do_nothing_cl, 1);
}
// Unload Klasses, String, Code Cache, etc.

View File

@ -41,7 +41,7 @@
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/preservedMarks.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "logging/log.hpp"
#include "runtime/biasedLocking.hpp"
#include "runtime/handles.inline.hpp"
@ -214,8 +214,8 @@ void G1FullCollector::phase1_mark_live_objects() {
// Weak oops cleanup.
{
GCTraceTime(Debug, gc, phases) trace("Phase 1: Weak Processing", scope()->timer());
WeakProcessor::weak_oops_do(&_is_alive, &do_nothing_cl);
GCTraceTime(Debug, gc, phases) debug("Phase 1: Weak Processing", scope()->timer());
WeakProcessor::weak_oops_do(_heap->workers(), &_is_alive, &do_nothing_cl, 1);
}
// Class unloading and cleanup.

View File

@ -33,8 +33,10 @@
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "logging/log.hpp"
#include "memory/iterator.inline.hpp"
#include "runtime/atomic.hpp"
class G1AdjustLiveClosure : public StackObj {
G1AdjustClosure* _adjust_closure;
@ -79,6 +81,8 @@ class G1AdjustRegionClosure : public HeapRegionClosure {
G1FullGCAdjustTask::G1FullGCAdjustTask(G1FullCollector* collector) :
G1FullGCTask("G1 Adjust", collector),
_root_processor(G1CollectedHeap::heap(), collector->workers()),
_references_done(0),
_weak_proc_task(collector->workers()),
_hrclaimer(collector->workers()),
_adjust(),
_adjust_string_dedup(NULL, &_adjust, G1StringDedup::is_enabled()) {
@ -94,12 +98,17 @@ void G1FullGCAdjustTask::work(uint worker_id) {
G1FullGCMarker* marker = collector()->marker(worker_id);
marker->preserved_stack()->adjust_during_full_gc();
// Adjust the weak_roots.
// Adjust the weak roots.
if (Atomic::add(1u, &_references_done) == 1u) { // First incr claims task.
G1CollectedHeap::heap()->ref_processor_stw()->weak_oops_do(&_adjust);
}
AlwaysTrueClosure always_alive;
_weak_proc_task.work(worker_id, &always_alive, &_adjust);
CLDToOopClosure adjust_cld(&_adjust);
CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
_root_processor.process_full_gc_weak_roots(&_adjust);
// Needs to be last, process_all_roots calls all_tasks_completed(...).
_root_processor.process_all_roots(
&_adjust,
&adjust_cld,

View File

@ -30,12 +30,16 @@
#include "gc/g1/g1RootProcessor.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/heapRegionManager.hpp"
#include "gc/shared/weakProcessorPhaseTimes.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "utilities/ticks.hpp"
class G1CollectedHeap;
class G1FullGCAdjustTask : public G1FullGCTask {
G1RootProcessor _root_processor;
volatile uint _references_done; // Atomic counter / bool
WeakProcessor::Task _weak_proc_task;
HeapRegionClaimer _hrclaimer;
G1AdjustClosure _adjust;
G1StringDedupUnlinkOrOopsDoClosure _adjust_string_dedup;

View File

@ -28,6 +28,7 @@
#include "gc/g1/g1HotCardCache.hpp"
#include "gc/g1/g1ParScanThreadState.inline.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/workerDataArray.inline.hpp"
#include "memory/resourceArea.hpp"
#include "logging/log.hpp"
@ -42,7 +43,8 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) :
_max_gc_threads(max_gc_threads),
_gc_start_counter(0),
_gc_pause_time_ms(0.0),
_ref_phase_times((GCTimer*)gc_timer, max_gc_threads)
_ref_phase_times(gc_timer, max_gc_threads),
_weak_phase_times(max_gc_threads)
{
assert(max_gc_threads > 0, "Must have some GC threads");
@ -129,7 +131,6 @@ void G1GCPhaseTimes::reset() {
_cur_clear_ct_time_ms = 0.0;
_cur_expand_heap_time_ms = 0.0;
_cur_ref_proc_time_ms = 0.0;
_cur_weak_ref_proc_time_ms = 0.0;
_cur_collection_start_sec = 0.0;
_root_region_scan_wait_time_ms = 0.0;
_external_accounted_time_ms = 0.0;
@ -157,6 +158,7 @@ void G1GCPhaseTimes::reset() {
}
_ref_phase_times.reset();
_weak_phase_times.reset();
}
void G1GCPhaseTimes::note_gc_start() {
@ -381,7 +383,7 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set() const {
_cur_collection_code_root_fixup_time_ms +
_recorded_preserve_cm_referents_time_ms +
_cur_ref_proc_time_ms +
_cur_weak_ref_proc_time_ms +
(_weak_phase_times.total_time_sec() * MILLIUNITS) +
_cur_clear_ct_time_ms +
_recorded_merge_pss_time_ms +
_cur_strong_code_root_purge_time_ms +
@ -399,8 +401,7 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set() const {
debug_time_for_reference("Reference Processing", _cur_ref_proc_time_ms);
_ref_phase_times.print_all_references(2, false);
debug_time("Weak Processing", _cur_weak_ref_proc_time_ms);
_weak_phase_times.log_print(2);
if (G1StringDedup::is_enabled()) {
debug_time("String Dedup Fixup", _cur_string_dedup_fixup_time_ms);

View File

@ -26,6 +26,7 @@
#define SHARE_VM_GC_G1_G1GCPHASETIMES_HPP
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
#include "gc/shared/weakProcessorPhaseTimes.hpp"
#include "logging/logLevel.hpp"
#include "memory/allocation.hpp"
#include "utilities/macros.hpp"
@ -127,9 +128,6 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
double _cur_clear_ct_time_ms;
double _cur_expand_heap_time_ms;
double _cur_ref_proc_time_ms;
double _cur_ref_enq_time_ms;
double _cur_weak_ref_proc_time_ms;
double _cur_collection_start_sec;
double _root_region_scan_wait_time_ms;
@ -163,6 +161,7 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
double _cur_verify_after_time_ms;
ReferenceProcessorPhaseTimes _ref_phase_times;
WeakProcessorPhaseTimes _weak_phase_times;
double worker_time(GCParPhases phase, uint worker);
void note_gc_end();
@ -257,10 +256,6 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
_cur_ref_proc_time_ms = ms;
}
void record_weak_ref_proc_time(double ms) {
_cur_weak_ref_proc_time_ms = ms;
}
void record_root_region_scan_wait_time(double time_ms) {
_root_region_scan_wait_time_ms = time_ms;
}
@ -365,6 +360,8 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
}
ReferenceProcessorPhaseTimes* ref_phase_times() { return &_ref_phase_times; }
WeakProcessorPhaseTimes* weak_phase_times() { return &_weak_phase_times; }
};
class G1EvacPhaseWithTrimTimeTracker : public StackObj {

View File

@ -40,7 +40,6 @@
#include "gc/g1/heapRegion.inline.hpp"
#include "gc/shared/oopStorageParState.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/mutex.hpp"
#include "services/management.hpp"
@ -314,16 +313,6 @@ void G1RootProcessor::process_code_cache_roots(CodeBlobClosure* code_closure,
}
}
void G1RootProcessor::process_full_gc_weak_roots(OopClosure* oops) {
if (!_process_strong_tasks.is_task_claimed(G1RP_PS_refProcessor_oops_do)) {
_g1h->ref_processor_stw()->weak_oops_do(oops);
}
if (!_process_strong_tasks.is_task_claimed(G1RP_PS_weakProcessor_oops_do)) {
WeakProcessor::oops_do(oops);
}
}
uint G1RootProcessor::n_workers() const {
return _srs.n_threads();
}

View File

@ -68,7 +68,6 @@ class G1RootProcessor : public StackObj {
G1RP_PS_aot_oops_do,
G1RP_PS_filter_satb_buffers,
G1RP_PS_refProcessor_oops_do,
G1RP_PS_weakProcessor_oops_do,
// Leave this one last.
G1RP_PS_NumElements
};
@ -122,10 +121,6 @@ public:
CLDClosure* clds,
CodeBlobClosure* blobs);
// Apply closure to weak roots in the system. Used during the adjust phase
// for the Full GC.
void process_full_gc_weak_roots(OopClosure* oops);
// Number of worker threads used by the root processor.
uint n_workers() const;
};

View File

@ -23,24 +23,107 @@
*/
#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "gc/shared/oopStorage.inline.hpp"
#include "gc/shared/oopStorageParState.inline.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/jniHandles.hpp"
#include "gc/shared/weakProcessor.inline.hpp"
#include "gc/shared/weakProcessorPhases.hpp"
#include "gc/shared/weakProcessorPhaseTimes.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/iterator.hpp"
#include "runtime/globals.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
void WeakProcessor::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive) {
JNIHandles::weak_oops_do(is_alive, keep_alive);
JvmtiExport::weak_oops_do(is_alive, keep_alive);
SystemDictionary::vm_weak_oop_storage()->weak_oops_do(is_alive, keep_alive);
JFR_ONLY(Jfr::weak_oops_do(is_alive, keep_alive);)
FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
if (WeakProcessorPhases::is_serial(phase)) {
WeakProcessorPhases::processor(phase)(is_alive, keep_alive);
} else {
WeakProcessorPhases::oop_storage(phase)->weak_oops_do(is_alive, keep_alive);
}
}
}
void WeakProcessor::oops_do(OopClosure* closure) {
AlwaysTrueClosure always_true;
weak_oops_do(&always_true, closure);
}
uint WeakProcessor::ergo_workers(uint max_workers) {
// Ignore ParallelRefProcEnabled; that's for j.l.r.Reference processing.
if (ReferencesPerThread == 0) {
// Configuration says always use all the threads.
return max_workers;
}
// One thread per ReferencesPerThread references (or fraction thereof)
// in the various OopStorage objects, bounded by max_threads.
//
// Serial phases are ignored in this calculation, because of the
// cost of running unnecessary threads. These phases are normally
// small or empty (assuming they are configured to exist at all),
// and development oriented, so not allocating any threads
// specifically for them is okay.
size_t ref_count = 0;
FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(phase) {
ref_count += WeakProcessorPhases::oop_storage(phase)->allocation_count();
}
// +1 to (approx) round up the ref per thread division.
size_t nworkers = 1 + (ref_count / ReferencesPerThread);
nworkers = MIN2(nworkers, static_cast<size_t>(max_workers));
return static_cast<uint>(nworkers);
}
void WeakProcessor::Task::initialize() {
assert(_nworkers != 0, "must be");
assert(_phase_times == NULL || _nworkers <= _phase_times->max_threads(),
"nworkers (%u) exceeds max threads (%u)",
_nworkers, _phase_times->max_threads());
if (_phase_times) {
_phase_times->set_active_workers(_nworkers);
}
uint storage_count = WeakProcessorPhases::oop_storage_phase_count;
_storage_states = NEW_C_HEAP_ARRAY(StorageState, storage_count, mtGC);
StorageState* states = _storage_states;
FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(phase) {
OopStorage* storage = WeakProcessorPhases::oop_storage(phase);
new (states++) StorageState(storage, _nworkers);
}
}
WeakProcessor::Task::Task(uint nworkers) :
_phase_times(NULL),
_nworkers(nworkers),
_serial_phases_done(WeakProcessorPhases::serial_phase_count),
_storage_states(NULL)
{
initialize();
}
WeakProcessor::Task::Task(WeakProcessorPhaseTimes* phase_times, uint nworkers) :
_phase_times(phase_times),
_nworkers(nworkers),
_serial_phases_done(WeakProcessorPhases::serial_phase_count),
_storage_states(NULL)
{
initialize();
}
WeakProcessor::Task::~Task() {
if (_storage_states != NULL) {
StorageState* states = _storage_states;
FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(phase) {
states->StorageState::~StorageState();
++states;
}
FREE_C_HEAP_ARRAY(StorageState, _storage_states);
}
}
void WeakProcessor::GangTask::work(uint worker_id) {
_erased_do_work(this, worker_id);
}

View File

@ -25,8 +25,12 @@
#ifndef SHARE_VM_GC_SHARED_WEAKPROCESSOR_HPP
#define SHARE_VM_GC_SHARED_WEAKPROCESSOR_HPP
#include "gc/shared/oopStorageParState.hpp"
#include "gc/shared/workgroup.hpp"
#include "memory/allocation.hpp"
#include "memory/iterator.hpp"
class WeakProcessorPhaseTimes;
class WorkGang;
// Helper class to aid in root scanning and cleaning of weak oops in the VM.
//
@ -41,6 +45,51 @@ public:
// Visit all oop*s and apply the given closure.
static void oops_do(OopClosure* closure);
// Parallel version. Uses ergo_workers(), active workers, and
// phase_time's max_threads to determine the number of threads to use.
// IsAlive must be derived from BoolObjectClosure.
// KeepAlive must be derived from OopClosure.
template<typename IsAlive, typename KeepAlive>
static void weak_oops_do(WorkGang* workers,
IsAlive* is_alive,
KeepAlive* keep_alive,
WeakProcessorPhaseTimes* phase_times);
// Convenience parallel version. Uses ergo_workers() and active workers
// to determine the number of threads to run. Implicitly logs phase times.
// IsAlive must be derived from BoolObjectClosure.
// KeepAlive must be derived from OopClosure.
template<typename IsAlive, typename KeepAlive>
static void weak_oops_do(WorkGang* workers,
IsAlive* is_alive,
KeepAlive* keep_alive,
uint indent_log);
static uint ergo_workers(uint max_workers);
class Task;
private:
class GangTask;
};
class WeakProcessor::Task {
typedef OopStorage::ParState<false, false> StorageState;
WeakProcessorPhaseTimes* _phase_times;
uint _nworkers;
SubTasksDone _serial_phases_done;
StorageState* _storage_states;
void initialize();
public:
Task(uint nworkers); // No time tracking.
Task(WeakProcessorPhaseTimes* phase_times, uint nworkers);
~Task();
template<typename IsAlive, typename KeepAlive>
void work(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive);
};
#endif // SHARE_VM_GC_SHARED_WEAKPROCESSOR_HPP

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2018, 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_VM_GC_SHARED_WEAKPROCESSOR_INLINE_HPP
#define SHARE_VM_GC_SHARED_WEAKPROCESSOR_INLINE_HPP
#include "gc/shared/oopStorage.inline.hpp"
#include "gc/shared/oopStorageParState.inline.hpp"
#include "gc/shared/weakProcessor.hpp"
#include "gc/shared/weakProcessorPhases.hpp"
#include "gc/shared/weakProcessorPhaseTimes.hpp"
#include "gc/shared/workgroup.hpp"
#include "utilities/debug.hpp"
class BoolObjectClosure;
class OopClosure;
template<typename IsAlive, typename KeepAlive>
void WeakProcessor::Task::work(uint worker_id,
IsAlive* is_alive,
KeepAlive* keep_alive) {
assert(worker_id < _nworkers,
"worker_id (%u) exceeds task's configured workers (%u)",
worker_id, _nworkers);
FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
if (WeakProcessorPhases::is_serial(phase)) {
uint serial_index = WeakProcessorPhases::serial_index(phase);
if (!_serial_phases_done.is_task_claimed(serial_index)) {
WeakProcessorPhaseTimeTracker pt(_phase_times, phase);
WeakProcessorPhases::processor(phase)(is_alive, keep_alive);
}
} else {
WeakProcessorPhaseTimeTracker pt(_phase_times, phase, worker_id);
uint storage_index = WeakProcessorPhases::oop_storage_index(phase);
_storage_states[storage_index].weak_oops_do(is_alive, keep_alive);
}
}
_serial_phases_done.all_tasks_completed(_nworkers);
}
class WeakProcessor::GangTask : public AbstractGangTask {
Task _task;
BoolObjectClosure* _is_alive;
OopClosure* _keep_alive;
void (*_erased_do_work)(GangTask* task, uint worker_id);
template<typename IsAlive, typename KeepAlive>
static void erased_do_work(GangTask* task, uint worker_id) {
task->_task.work(worker_id,
static_cast<IsAlive*>(task->_is_alive),
static_cast<KeepAlive*>(task->_keep_alive));
}
public:
template<typename IsAlive, typename KeepAlive>
GangTask(const char* name,
IsAlive* is_alive,
KeepAlive* keep_alive,
WeakProcessorPhaseTimes* phase_times,
uint nworkers) :
AbstractGangTask(name),
_task(phase_times, nworkers),
_is_alive(is_alive),
_keep_alive(keep_alive),
_erased_do_work(&erased_do_work<IsAlive, KeepAlive>)
{}
virtual void work(uint worker_id);
};
template<typename IsAlive, typename KeepAlive>
void WeakProcessor::weak_oops_do(WorkGang* workers,
IsAlive* is_alive,
KeepAlive* keep_alive,
WeakProcessorPhaseTimes* phase_times) {
WeakProcessorTimeTracker tt(phase_times);
uint nworkers = ergo_workers(MIN2(workers->active_workers(),
phase_times->max_threads()));
GangTask task("Weak Processor", is_alive, keep_alive, phase_times, nworkers);
workers->run_task(&task, nworkers);
}
template<typename IsAlive, typename KeepAlive>
void WeakProcessor::weak_oops_do(WorkGang* workers,
IsAlive* is_alive,
KeepAlive* keep_alive,
uint indent_log) {
uint nworkers = ergo_workers(workers->active_workers());
WeakProcessorPhaseTimes pt(nworkers);
weak_oops_do(workers, is_alive, keep_alive, &pt);
pt.log_print_phases(indent_log);
}
#endif // SHARE_VM_GC_SHARED_WEAKPROCESSOR_INLINE_HPP

View File

@ -0,0 +1,268 @@
/*
* Copyright (c) 2018, 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/shared/weakProcessorPhases.hpp"
#include "gc/shared/weakProcessorPhaseTimes.hpp"
#include "gc/shared/workerDataArray.inline.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ticks.hpp"
static uint phase_index(WeakProcessorPhase phase) {
return WeakProcessorPhases::index(phase);
}
static bool is_serial_phase(WeakProcessorPhase phase) {
return WeakProcessorPhases::is_serial(phase);
}
static void assert_oop_storage_phase(WeakProcessorPhase phase) {
assert(WeakProcessorPhases::is_oop_storage(phase),
"Not an oop_storage phase %u", phase_index(phase));
}
const double uninitialized_time = -1.0;
#ifdef ASSERT
static bool is_initialized_time(double t) { return t >= 0.0; }
#endif // ASSERT
static void reset_times(double* times, size_t ntimes) {
for (size_t i = 0; i < ntimes; ++i) {
times[i] = uninitialized_time;
}
}
WeakProcessorPhaseTimes::WeakProcessorPhaseTimes(uint max_threads) :
_max_threads(max_threads),
_active_workers(0),
_total_time_sec(uninitialized_time),
_worker_phase_times_sec()
{
assert(_max_threads > 0, "max_threads must not be zero");
reset_times(_phase_times_sec, ARRAY_SIZE(_phase_times_sec));
if (_max_threads > 1) {
WorkerDataArray<double>** wpt = _worker_phase_times_sec;
FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(phase) {
const char* description = WeakProcessorPhases::description(phase);
*wpt++ = new WorkerDataArray<double>(_max_threads, description);
}
}
}
WeakProcessorPhaseTimes::~WeakProcessorPhaseTimes() {
for (size_t i = 0; i < ARRAY_SIZE(_worker_phase_times_sec); ++i) {
delete _worker_phase_times_sec[i];
}
}
uint WeakProcessorPhaseTimes::max_threads() const { return _max_threads; }
uint WeakProcessorPhaseTimes::active_workers() const {
assert(_active_workers != 0, "active workers not set");
return _active_workers;
}
void WeakProcessorPhaseTimes::set_active_workers(uint n) {
assert(_active_workers == 0, "active workers already set");
assert(n > 0, "active workers must be non-zero");
assert(n <= _max_threads, "active workers must not exceed max threads");
_active_workers = n;
}
void WeakProcessorPhaseTimes::reset() {
_active_workers = 0;
_total_time_sec = uninitialized_time;
reset_times(_phase_times_sec, ARRAY_SIZE(_phase_times_sec));
if (_max_threads > 1) {
for (size_t i = 0; i < ARRAY_SIZE(_worker_phase_times_sec); ++i) {
_worker_phase_times_sec[i]->reset();
}
}
}
double WeakProcessorPhaseTimes::total_time_sec() const {
assert(is_initialized_time(_total_time_sec), "Total time not set");
return _total_time_sec;
}
void WeakProcessorPhaseTimes::record_total_time_sec(double time_sec) {
assert(!is_initialized_time(_total_time_sec), "Already set total time");
_total_time_sec = time_sec;
}
double WeakProcessorPhaseTimes::phase_time_sec(WeakProcessorPhase phase) const {
assert(is_initialized_time(_phase_times_sec[phase_index(phase)]),
"phase time not set %u", phase_index(phase));
return _phase_times_sec[phase_index(phase)];
}
void WeakProcessorPhaseTimes::record_phase_time_sec(WeakProcessorPhase phase, double time_sec) {
assert(!is_initialized_time(_phase_times_sec[phase_index(phase)]),
"Already set time for phase %u", phase_index(phase));
_phase_times_sec[phase_index(phase)] = time_sec;
}
WorkerDataArray<double>* WeakProcessorPhaseTimes::worker_data(WeakProcessorPhase phase) const {
assert_oop_storage_phase(phase);
assert(active_workers() > 1, "No worker data when single-threaded");
return _worker_phase_times_sec[WeakProcessorPhases::oop_storage_index(phase)];
}
double WeakProcessorPhaseTimes::worker_time_sec(uint worker_id, WeakProcessorPhase phase) const {
assert(worker_id < active_workers(),
"invalid worker id %u for %u", worker_id, active_workers());
if (active_workers() == 1) {
return phase_time_sec(phase);
} else {
return worker_data(phase)->get(worker_id);
}
}
void WeakProcessorPhaseTimes::record_worker_time_sec(uint worker_id,
WeakProcessorPhase phase,
double time_sec) {
if (active_workers() == 1) {
record_phase_time_sec(phase, time_sec);
} else {
worker_data(phase)->set(worker_id, time_sec);
}
}
static double elapsed_time_sec(Ticks start_time, Ticks end_time) {
return (end_time - start_time).seconds();
}
WeakProcessorTimeTracker::WeakProcessorTimeTracker(WeakProcessorPhaseTimes* times) :
_times(times),
_start_time(Ticks::now())
{}
WeakProcessorTimeTracker::~WeakProcessorTimeTracker() {
if (_times != NULL) {
Ticks end_time = Ticks::now();
_times->record_total_time_sec(elapsed_time_sec(_start_time, end_time));
}
}
WeakProcessorPhaseTimeTracker::WeakProcessorPhaseTimeTracker(WeakProcessorPhaseTimes* times,
WeakProcessorPhase phase,
uint worker_id) :
_times(times),
_phase(phase),
_worker_id(worker_id),
_start_time(Ticks::now())
{
assert_oop_storage_phase(_phase);
assert(_times == NULL || worker_id < _times->active_workers(),
"Invalid worker_id %u", worker_id);
}
WeakProcessorPhaseTimeTracker::WeakProcessorPhaseTimeTracker(WeakProcessorPhaseTimes* times,
WeakProcessorPhase phase) :
_times(times),
_phase(phase),
_worker_id(0),
_start_time(Ticks::now())
{
assert(is_serial_phase(phase), "Not a serial phase %u", phase_index(phase));
}
WeakProcessorPhaseTimeTracker::~WeakProcessorPhaseTimeTracker() {
if (_times != NULL) {
double time_sec = elapsed_time_sec(_start_time, Ticks::now());
if (is_serial_phase(_phase)) {
_times->record_phase_time_sec(_phase, time_sec);
} else {
_times->record_worker_time_sec(_worker_id, _phase, time_sec);
}
}
}
//////////////////////////////////////////////////////////////////////////////
// Printing times
const char* const indents[] = {"", " ", " ", " ", " "};
const size_t max_indents_index = ARRAY_SIZE(indents) - 1;
static const char* indent_str(size_t i) {
return indents[MIN2(i, max_indents_index)];
}
#define TIME_FORMAT "%.1lfms"
void WeakProcessorPhaseTimes::log_st_phase(WeakProcessorPhase phase,
uint indent) const {
log_debug(gc, phases)("%s%s: " TIME_FORMAT,
indent_str(indent),
WeakProcessorPhases::description(phase),
phase_time_sec(phase) * MILLIUNITS);
}
void WeakProcessorPhaseTimes::log_mt_phase_summary(WeakProcessorPhase phase,
uint indent) const {
LogTarget(Debug, gc, phases) lt;
LogStream ls(lt);
ls.print("%s", indents[indent]);
worker_data(phase)->print_summary_on(&ls, true);
}
void WeakProcessorPhaseTimes::log_mt_phase_details(WeakProcessorPhase phase,
uint indent) const {
LogTarget(Trace, gc, phases) lt;
LogStream ls(lt);
ls.print("%s", indents[indent]);
worker_data(phase)->print_details_on(&ls);
}
void WeakProcessorPhaseTimes::log_print_phases(uint indent) const {
if (log_is_enabled(Debug, gc, phases)) {
bool details_enabled = log_is_enabled(Trace, gc, phases);
FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
if (is_serial_phase(phase) || (active_workers() == 1)) {
log_st_phase(phase, indent);
} else {
log_mt_phase_summary(phase, indent);
if (details_enabled) {
log_mt_phase_details(phase, indent + 1);
}
}
}
}
}
void WeakProcessorPhaseTimes::log_print(uint indent) const {
if (log_is_enabled(Debug, gc, phases)) {
log_debug(gc, phases)("%s%s: " TIME_FORMAT,
indent_str(indent),
"Weak Processing",
total_time_sec() * MILLIUNITS);
log_print_phases(indent + 1);
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2018, 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_SHARED_WEAKPROCESSORPHASETIMES_HPP
#define SHARE_GC_SHARED_WEAKPROCESSORPHASETIMES_HPP
#include "gc/shared/weakProcessorPhases.hpp"
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ticks.hpp"
template<typename T> class WorkerDataArray;
class WeakProcessorPhaseTimes : public CHeapObj<mtGC> {
uint _max_threads;
uint _active_workers;
// Total time for weak processor.
double _total_time_sec;
// Total time for each serially processed phase. Entries for phases
// processed by multiple threads are unused, as are entries for
// unexecuted phases.
double _phase_times_sec[WeakProcessorPhases::phase_count];
// Per-worker times, if multiple threads used and the phase was executed.
WorkerDataArray<double>* _worker_phase_times_sec[WeakProcessorPhases::oop_storage_phase_count];
WorkerDataArray<double>* worker_data(WeakProcessorPhase phase) const;
void log_st_phase(WeakProcessorPhase phase, uint indent) const;
void log_mt_phase_summary(WeakProcessorPhase phase, uint indent) const;
void log_mt_phase_details(WeakProcessorPhase phase, uint indent) const;
public:
WeakProcessorPhaseTimes(uint max_threads);
~WeakProcessorPhaseTimes();
uint max_threads() const;
uint active_workers() const;
void set_active_workers(uint n);
double total_time_sec() const;
double phase_time_sec(WeakProcessorPhase phase) const;
double worker_time_sec(uint worker_id, WeakProcessorPhase phase) const;
void record_total_time_sec(double time_sec);
void record_phase_time_sec(WeakProcessorPhase phase, double time_sec);
void record_worker_time_sec(uint worker_id, WeakProcessorPhase phase, double time_sec);
void reset();
void log_print(uint indent = 0) const;
void log_print_phases(uint indent = 0) const;
};
// Record total weak processor time and worker count in times.
// Does nothing if times is NULL.
class WeakProcessorTimeTracker : StackObj {
WeakProcessorPhaseTimes* _times;
Ticks _start_time;
public:
WeakProcessorTimeTracker(WeakProcessorPhaseTimes* times);
~WeakProcessorTimeTracker();
};
// Record phase time contribution for the current thread in phase times.
// Does nothing if phase times is NULL.
class WeakProcessorPhaseTimeTracker : StackObj {
private:
WeakProcessorPhaseTimes* _times;
WeakProcessorPhase _phase;
uint _worker_id;
Ticks _start_time;
public:
// For tracking serial phase times.
// Precondition: WeakProcessorPhases::is_serial(phase)
WeakProcessorPhaseTimeTracker(WeakProcessorPhaseTimes* times,
WeakProcessorPhase phase);
// For tracking possibly parallel phase times (even if processed by
// only one thread).
// Precondition: WeakProcessorPhases::is_oop_storage(phase)
// Precondition: worker_id < times->max_threads().
WeakProcessorPhaseTimeTracker(WeakProcessorPhaseTimes* times,
WeakProcessorPhase phase,
uint worker_id);
~WeakProcessorPhaseTimeTracker();
};
#endif // SHARE_GC_SHARED_WEAKPROCESSORPHASETIMES_HPP

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2018, 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 "classfile/systemDictionary.hpp"
#include "gc/shared/weakProcessorPhases.hpp"
#include "runtime/jniHandles.hpp"
#include "utilities/debug.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif // INCLUDE_JFR
#if INCLUDE_JVMTI
#include "prims/jvmtiExport.hpp"
#endif // INCLUDE_JVMTI
WeakProcessorPhases::Phase WeakProcessorPhases::phase(uint value) {
assert(value < phase_count, "Invalid phase value %u", value);
return static_cast<Phase>(value);
}
uint WeakProcessorPhases::index(Phase phase) {
uint value = static_cast<uint>(phase);
assert(value < phase_count, "Invalid phase %u", value);
return value;
}
uint WeakProcessorPhases::serial_index(Phase phase) {
assert(is_serial(phase), "not serial phase %u", index(phase));
return index(phase) - serial_phase_start;
}
uint WeakProcessorPhases::oop_storage_index(Phase phase) {
assert(is_oop_storage(phase), "not oop storage phase %u", index(phase));
return index(phase) - oop_storage_phase_start;
}
bool WeakProcessorPhases::is_serial(Phase phase) {
return (index(phase) - serial_phase_start) < serial_phase_count;
}
bool WeakProcessorPhases::is_oop_storage(Phase phase) {
return (index(phase) - oop_storage_phase_start) < oop_storage_phase_count;
}
const char* WeakProcessorPhases::description(Phase phase) {
switch (phase) {
JVMTI_ONLY(case jvmti: return "JVMTI weak processing";)
JFR_ONLY(case jfr: return "JFR weak processing";)
case jni: return "JNI weak processing";
case vm: return "VM weak processing";
default:
ShouldNotReachHere();
return "Invalid weak processing phase";
}
}
WeakProcessorPhases::Processor WeakProcessorPhases::processor(Phase phase) {
switch (phase) {
JVMTI_ONLY(case jvmti: return &JvmtiExport::weak_oops_do;)
JFR_ONLY(case jfr: return &Jfr::weak_oops_do;)
default:
ShouldNotReachHere();
return NULL;
}
}
OopStorage* WeakProcessorPhases::oop_storage(Phase phase) {
switch (phase) {
case jni: return JNIHandles::weak_global_handles();
case vm: return SystemDictionary::vm_weak_oop_storage();
default:
ShouldNotReachHere();
return NULL;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2018, 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_SHARED_WEAKPROCESSORPHASES_HPP
#define SHARE_GC_SHARED_WEAKPROCESSORPHASES_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
class BoolObjectClosure;
class OopClosure;
class OopStorage;
class WeakProcessorPhases : AllStatic {
public:
typedef void (*Processor)(BoolObjectClosure*, OopClosure*);
enum Phase {
// Serial phases.
JVMTI_ONLY(jvmti COMMA)
JFR_ONLY(jfr COMMA)
// OopStorage phases.
jni,
vm
};
static const uint serial_phase_start = 0;
static const uint serial_phase_count = jni;
static const uint oop_storage_phase_start = serial_phase_count;
static const uint oop_storage_phase_count = (vm + 1) - oop_storage_phase_start;
static const uint phase_count = serial_phase_count + oop_storage_phase_count;
static Phase phase(uint value);
static uint index(Phase phase);
// Indexes relative to the corresponding phase_start constant.
static uint serial_index(Phase phase);
static uint oop_storage_index(Phase phase);
static bool is_serial(Phase phase);
static bool is_oop_storage(Phase phase);
static const char* description(Phase phase);
static Processor processor(Phase phase); // Precondition: is_serial(phase)
static OopStorage* oop_storage(Phase phase); // Precondition: is_oop_storage(phase)
};
typedef WeakProcessorPhases::Phase WeakProcessorPhase;
#define FOR_EACH_WEAK_PROCESSOR_PHASE(P) \
for (WeakProcessorPhase P = static_cast<WeakProcessorPhase>(0); \
static_cast<uint>(P) < WeakProcessorPhases::phase_count; \
P = static_cast<WeakProcessorPhase>(static_cast<uint>(P) + 1))
#define FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(P) \
for (WeakProcessorPhase P = static_cast<WeakProcessorPhase>(WeakProcessorPhases::oop_storage_phase_start); \
static_cast<uint>(P) < (WeakProcessorPhases::oop_storage_phase_start + \
WeakProcessorPhases::oop_storage_phase_count); \
P = static_cast<WeakProcessorPhase>(static_cast<uint>(P) + 1))
#endif // SHARE_GC_SHARED_WEAKPROCESSORPHASES_HPP

View File

@ -462,7 +462,7 @@ void SubTasksDone::all_tasks_completed(uint n_threads) {
SubTasksDone::~SubTasksDone() {
if (_tasks != NULL) FREE_C_HEAP_ARRAY(jint, _tasks);
if (_tasks != NULL) FREE_C_HEAP_ARRAY(uint, _tasks);
}
// *** SequentialSubTasksDone