mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
6858051: Create GC worker threads dynamically
Reviewed-by: tschatzl, drwhite
This commit is contained in:
parent
af168abecc
commit
c16b4b7673
9 changed files with 196 additions and 65 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,8 +25,8 @@
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "gc/parallel/gcTaskManager.hpp"
|
#include "gc/parallel/gcTaskManager.hpp"
|
||||||
#include "gc/parallel/gcTaskThread.hpp"
|
#include "gc/parallel/gcTaskThread.hpp"
|
||||||
#include "gc/shared/adaptiveSizePolicy.hpp"
|
|
||||||
#include "gc/shared/gcId.hpp"
|
#include "gc/shared/gcId.hpp"
|
||||||
|
#include "gc/shared/workerManager.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/allocation.hpp"
|
#include "memory/allocation.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
|
@ -34,6 +34,7 @@
|
||||||
#include "runtime/mutex.hpp"
|
#include "runtime/mutex.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/orderAccess.inline.hpp"
|
#include "runtime/orderAccess.inline.hpp"
|
||||||
|
#include "runtime/os.hpp"
|
||||||
|
|
||||||
//
|
//
|
||||||
// GCTask
|
// GCTask
|
||||||
|
@ -372,10 +373,28 @@ SynchronizedGCTaskQueue::~SynchronizedGCTaskQueue() {
|
||||||
GCTaskManager::GCTaskManager(uint workers) :
|
GCTaskManager::GCTaskManager(uint workers) :
|
||||||
_workers(workers),
|
_workers(workers),
|
||||||
_active_workers(0),
|
_active_workers(0),
|
||||||
_idle_workers(0) {
|
_idle_workers(0),
|
||||||
|
_created_workers(0) {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GCTaskThread* GCTaskManager::install_worker(uint t) {
|
||||||
|
GCTaskThread* new_worker = GCTaskThread::create(this, t, _processor_assignment[t]);
|
||||||
|
set_thread(t, new_worker);
|
||||||
|
return new_worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCTaskManager::add_workers(bool initializing) {
|
||||||
|
os::ThreadType worker_type = os::pgc_thread;
|
||||||
|
_created_workers = WorkerManager::add_workers(this,
|
||||||
|
_active_workers,
|
||||||
|
(uint) _workers,
|
||||||
|
_created_workers,
|
||||||
|
worker_type,
|
||||||
|
initializing);
|
||||||
|
_active_workers = MIN2(_created_workers, _active_workers);
|
||||||
|
}
|
||||||
|
|
||||||
void GCTaskManager::initialize() {
|
void GCTaskManager::initialize() {
|
||||||
if (TraceGCTaskManager) {
|
if (TraceGCTaskManager) {
|
||||||
tty->print_cr("GCTaskManager::initialize: workers: %u", workers());
|
tty->print_cr("GCTaskManager::initialize: workers: %u", workers());
|
||||||
|
@ -394,28 +413,30 @@ void GCTaskManager::initialize() {
|
||||||
// Set up worker threads.
|
// Set up worker threads.
|
||||||
// Distribute the workers among the available processors,
|
// Distribute the workers among the available processors,
|
||||||
// unless we were told not to, or if the os doesn't want to.
|
// unless we were told not to, or if the os doesn't want to.
|
||||||
uint* processor_assignment = NEW_C_HEAP_ARRAY(uint, workers(), mtGC);
|
_processor_assignment = NEW_C_HEAP_ARRAY(uint, workers(), mtGC);
|
||||||
if (!BindGCTaskThreadsToCPUs ||
|
if (!BindGCTaskThreadsToCPUs ||
|
||||||
!os::distribute_processes(workers(), processor_assignment)) {
|
!os::distribute_processes(workers(), _processor_assignment)) {
|
||||||
for (uint a = 0; a < workers(); a += 1) {
|
for (uint a = 0; a < workers(); a += 1) {
|
||||||
processor_assignment[a] = sentinel_worker();
|
_processor_assignment[a] = sentinel_worker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_thread = NEW_C_HEAP_ARRAY(GCTaskThread*, workers(), mtGC);
|
_thread = NEW_C_HEAP_ARRAY(GCTaskThread*, workers(), mtGC);
|
||||||
for (uint t = 0; t < workers(); t += 1) {
|
_active_workers = ParallelGCThreads;
|
||||||
set_thread(t, GCTaskThread::create(this, t, processor_assignment[t]));
|
if (UseDynamicNumberOfGCThreads && !FLAG_IS_CMDLINE(ParallelGCThreads)) {
|
||||||
|
_active_workers = 1U;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(gc, task, thread) log;
|
Log(gc, task, thread) log;
|
||||||
if (log.is_trace()) {
|
if (log.is_trace()) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
outputStream* out = log.trace_stream();
|
outputStream* out = log.trace_stream();
|
||||||
out->print("GCTaskManager::initialize: distribution:");
|
out->print("GCTaskManager::initialize: distribution:");
|
||||||
for (uint t = 0; t < workers(); t += 1) {
|
for (uint t = 0; t < workers(); t += 1) {
|
||||||
out->print(" %u", processor_assignment[t]);
|
out->print(" %u", _processor_assignment[t]);
|
||||||
}
|
}
|
||||||
out->cr();
|
out->cr();
|
||||||
}
|
}
|
||||||
FREE_C_HEAP_ARRAY(uint, processor_assignment);
|
|
||||||
}
|
}
|
||||||
reset_busy_workers();
|
reset_busy_workers();
|
||||||
set_unblocked();
|
set_unblocked();
|
||||||
|
@ -426,9 +447,8 @@ void GCTaskManager::initialize() {
|
||||||
reset_completed_tasks();
|
reset_completed_tasks();
|
||||||
reset_barriers();
|
reset_barriers();
|
||||||
reset_emptied_queue();
|
reset_emptied_queue();
|
||||||
for (uint s = 0; s < workers(); s += 1) {
|
|
||||||
thread(s)->start();
|
add_workers(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GCTaskManager::~GCTaskManager() {
|
GCTaskManager::~GCTaskManager() {
|
||||||
|
@ -437,13 +457,17 @@ GCTaskManager::~GCTaskManager() {
|
||||||
NoopGCTask::destroy(_noop_task);
|
NoopGCTask::destroy(_noop_task);
|
||||||
_noop_task = NULL;
|
_noop_task = NULL;
|
||||||
if (_thread != NULL) {
|
if (_thread != NULL) {
|
||||||
for (uint i = 0; i < workers(); i += 1) {
|
for (uint i = 0; i < created_workers(); i += 1) {
|
||||||
GCTaskThread::destroy(thread(i));
|
GCTaskThread::destroy(thread(i));
|
||||||
set_thread(i, NULL);
|
set_thread(i, NULL);
|
||||||
}
|
}
|
||||||
FREE_C_HEAP_ARRAY(GCTaskThread*, _thread);
|
FREE_C_HEAP_ARRAY(GCTaskThread*, _thread);
|
||||||
_thread = NULL;
|
_thread = NULL;
|
||||||
}
|
}
|
||||||
|
if (_processor_assignment != NULL) {
|
||||||
|
FREE_C_HEAP_ARRAY(uint, _processor_assignment);
|
||||||
|
_processor_assignment = NULL;
|
||||||
|
}
|
||||||
if (_resource_flag != NULL) {
|
if (_resource_flag != NULL) {
|
||||||
FREE_C_HEAP_ARRAY(bool, _resource_flag);
|
FREE_C_HEAP_ARRAY(bool, _resource_flag);
|
||||||
_resource_flag = NULL;
|
_resource_flag = NULL;
|
||||||
|
@ -470,6 +494,9 @@ void GCTaskManager::set_active_gang() {
|
||||||
"all_workers_active() is incorrect: "
|
"all_workers_active() is incorrect: "
|
||||||
"active %d ParallelGCThreads %u", active_workers(),
|
"active %d ParallelGCThreads %u", active_workers(),
|
||||||
ParallelGCThreads);
|
ParallelGCThreads);
|
||||||
|
_active_workers = MIN2(_active_workers, _workers);
|
||||||
|
// "add_workers" does not guarantee any additional workers
|
||||||
|
add_workers(false);
|
||||||
log_trace(gc, task)("GCTaskManager::set_active_gang(): "
|
log_trace(gc, task)("GCTaskManager::set_active_gang(): "
|
||||||
"all_workers_active() %d workers %d "
|
"all_workers_active() %d workers %d "
|
||||||
"active %d ParallelGCThreads %u",
|
"active %d ParallelGCThreads %u",
|
||||||
|
@ -499,7 +526,7 @@ void GCTaskManager::task_idle_workers() {
|
||||||
// is starting). Try later to release enough idle_workers
|
// is starting). Try later to release enough idle_workers
|
||||||
// to allow the desired number of active_workers.
|
// to allow the desired number of active_workers.
|
||||||
more_inactive_workers =
|
more_inactive_workers =
|
||||||
workers() - active_workers() - idle_workers();
|
created_workers() - active_workers() - idle_workers();
|
||||||
if (more_inactive_workers < 0) {
|
if (more_inactive_workers < 0) {
|
||||||
int reduced_active_workers = active_workers() + more_inactive_workers;
|
int reduced_active_workers = active_workers() + more_inactive_workers;
|
||||||
set_active_workers(reduced_active_workers);
|
set_active_workers(reduced_active_workers);
|
||||||
|
@ -507,7 +534,7 @@ void GCTaskManager::task_idle_workers() {
|
||||||
}
|
}
|
||||||
log_trace(gc, task)("JT: %d workers %d active %d idle %d more %d",
|
log_trace(gc, task)("JT: %d workers %d active %d idle %d more %d",
|
||||||
Threads::number_of_non_daemon_threads(),
|
Threads::number_of_non_daemon_threads(),
|
||||||
workers(),
|
created_workers(),
|
||||||
active_workers(),
|
active_workers(),
|
||||||
idle_workers(),
|
idle_workers(),
|
||||||
more_inactive_workers);
|
more_inactive_workers);
|
||||||
|
@ -517,7 +544,7 @@ void GCTaskManager::task_idle_workers() {
|
||||||
q->enqueue(IdleGCTask::create_on_c_heap());
|
q->enqueue(IdleGCTask::create_on_c_heap());
|
||||||
increment_idle_workers();
|
increment_idle_workers();
|
||||||
}
|
}
|
||||||
assert(workers() == active_workers() + idle_workers(),
|
assert(created_workers() == active_workers() + idle_workers(),
|
||||||
"total workers should equal active + inactive");
|
"total workers should equal active + inactive");
|
||||||
add_list(q);
|
add_list(q);
|
||||||
// GCTaskQueue* q was created in a ResourceArea so a
|
// GCTaskQueue* q was created in a ResourceArea so a
|
||||||
|
@ -539,14 +566,15 @@ void GCTaskManager::print_task_time_stamps() {
|
||||||
if (!log_is_enabled(Debug, gc, task, time)) {
|
if (!log_is_enabled(Debug, gc, task, time)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for(uint i=0; i<ParallelGCThreads; i++) {
|
uint num_thr = created_workers();
|
||||||
|
for(uint i=0; i < num_thr; i++) {
|
||||||
GCTaskThread* t = thread(i);
|
GCTaskThread* t = thread(i);
|
||||||
t->print_task_time_stamps();
|
t->print_task_time_stamps();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCTaskManager::print_threads_on(outputStream* st) {
|
void GCTaskManager::print_threads_on(outputStream* st) {
|
||||||
uint num_thr = workers();
|
uint num_thr = created_workers();
|
||||||
for (uint i = 0; i < num_thr; i++) {
|
for (uint i = 0; i < num_thr; i++) {
|
||||||
thread(i)->print_on(st);
|
thread(i)->print_on(st);
|
||||||
st->cr();
|
st->cr();
|
||||||
|
@ -555,19 +583,20 @@ void GCTaskManager::print_threads_on(outputStream* st) {
|
||||||
|
|
||||||
void GCTaskManager::threads_do(ThreadClosure* tc) {
|
void GCTaskManager::threads_do(ThreadClosure* tc) {
|
||||||
assert(tc != NULL, "Null ThreadClosure");
|
assert(tc != NULL, "Null ThreadClosure");
|
||||||
uint num_thr = workers();
|
uint num_thr = created_workers();
|
||||||
for (uint i = 0; i < num_thr; i++) {
|
for (uint i = 0; i < num_thr; i++) {
|
||||||
tc->do_thread(thread(i));
|
tc->do_thread(thread(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GCTaskThread* GCTaskManager::thread(uint which) {
|
GCTaskThread* GCTaskManager::thread(uint which) {
|
||||||
assert(which < workers(), "index out of bounds");
|
assert(which < created_workers(), "index out of bounds");
|
||||||
assert(_thread[which] != NULL, "shouldn't have null thread");
|
assert(_thread[which] != NULL, "shouldn't have null thread");
|
||||||
return _thread[which];
|
return _thread[which];
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCTaskManager::set_thread(uint which, GCTaskThread* value) {
|
void GCTaskManager::set_thread(uint which, GCTaskThread* value) {
|
||||||
|
// "_created_workers" may not have been updated yet so use workers()
|
||||||
assert(which < workers(), "index out of bounds");
|
assert(which < workers(), "index out of bounds");
|
||||||
assert(value != NULL, "shouldn't have null thread");
|
assert(value != NULL, "shouldn't have null thread");
|
||||||
_thread[which] = value;
|
_thread[which] = value;
|
||||||
|
@ -728,7 +757,7 @@ uint GCTaskManager::decrement_busy_workers() {
|
||||||
|
|
||||||
void GCTaskManager::release_all_resources() {
|
void GCTaskManager::release_all_resources() {
|
||||||
// If you want this to be done atomically, do it in a WaitForBarrierGCTask.
|
// If you want this to be done atomically, do it in a WaitForBarrierGCTask.
|
||||||
for (uint i = 0; i < workers(); i += 1) {
|
for (uint i = 0; i < created_workers(); i += 1) {
|
||||||
set_resource_flag(i, true);
|
set_resource_flag(i, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -370,6 +370,7 @@ private:
|
||||||
Monitor* _monitor; // Notification of changes.
|
Monitor* _monitor; // Notification of changes.
|
||||||
SynchronizedGCTaskQueue* _queue; // Queue of tasks.
|
SynchronizedGCTaskQueue* _queue; // Queue of tasks.
|
||||||
GCTaskThread** _thread; // Array of worker threads.
|
GCTaskThread** _thread; // Array of worker threads.
|
||||||
|
uint _created_workers; // Number of workers created.
|
||||||
uint _active_workers; // Number of active workers.
|
uint _active_workers; // Number of active workers.
|
||||||
uint _busy_workers; // Number of busy workers.
|
uint _busy_workers; // Number of busy workers.
|
||||||
uint _blocking_worker; // The worker that's blocking.
|
uint _blocking_worker; // The worker that's blocking.
|
||||||
|
@ -381,6 +382,8 @@ private:
|
||||||
NoopGCTask* _noop_task; // The NoopGCTask instance.
|
NoopGCTask* _noop_task; // The NoopGCTask instance.
|
||||||
WaitHelper _wait_helper; // Used by inactive worker
|
WaitHelper _wait_helper; // Used by inactive worker
|
||||||
volatile uint _idle_workers; // Number of idled workers
|
volatile uint _idle_workers; // Number of idled workers
|
||||||
|
uint* _processor_assignment; // Worker to cpu mappings. May
|
||||||
|
// be used lazily
|
||||||
public:
|
public:
|
||||||
// Factory create and destroy methods.
|
// Factory create and destroy methods.
|
||||||
static GCTaskManager* create(uint workers) {
|
static GCTaskManager* create(uint workers) {
|
||||||
|
@ -546,6 +549,13 @@ protected:
|
||||||
uint active_workers() const {
|
uint active_workers() const {
|
||||||
return _active_workers;
|
return _active_workers;
|
||||||
}
|
}
|
||||||
|
uint created_workers() const {
|
||||||
|
return _created_workers;
|
||||||
|
}
|
||||||
|
// Create a GC worker and install into GCTaskManager
|
||||||
|
GCTaskThread* install_worker(uint worker_id);
|
||||||
|
// Add GC workers as needed.
|
||||||
|
void add_workers(bool initializing);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -44,9 +44,6 @@ GCTaskThread::GCTaskThread(GCTaskManager* manager,
|
||||||
_time_stamps(NULL),
|
_time_stamps(NULL),
|
||||||
_time_stamp_index(0)
|
_time_stamp_index(0)
|
||||||
{
|
{
|
||||||
if (!os::create_thread(this, os::pgc_thread))
|
|
||||||
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GC thread. Out of system resources.");
|
|
||||||
|
|
||||||
set_id(which);
|
set_id(which);
|
||||||
set_name("ParGC Thread#%d", which);
|
set_name("ParGC Thread#%d", which);
|
||||||
}
|
}
|
||||||
|
@ -57,10 +54,6 @@ GCTaskThread::~GCTaskThread() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCTaskThread::start() {
|
|
||||||
os::start_thread(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
GCTaskTimeStamp* GCTaskThread::time_stamp_at(uint index) {
|
GCTaskTimeStamp* GCTaskThread::time_stamp_at(uint index) {
|
||||||
guarantee(index < GCTaskTimeStampEntries, "increase GCTaskTimeStampEntries");
|
guarantee(index < GCTaskTimeStampEntries, "increase GCTaskTimeStampEntries");
|
||||||
if (_time_stamps == NULL) {
|
if (_time_stamps == NULL) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -48,13 +48,13 @@ private:
|
||||||
|
|
||||||
bool _is_working; // True if participating in GC tasks
|
bool _is_working; // True if participating in GC tasks
|
||||||
|
|
||||||
public:
|
|
||||||
// Factory create and destroy methods.
|
// Factory create and destroy methods.
|
||||||
static GCTaskThread* create(GCTaskManager* manager,
|
static GCTaskThread* create(GCTaskManager* manager,
|
||||||
uint which,
|
uint which,
|
||||||
uint processor_id) {
|
uint processor_id) {
|
||||||
return new GCTaskThread(manager, which, processor_id);
|
return new GCTaskThread(manager, which, processor_id);
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
static void destroy(GCTaskThread* manager) {
|
static void destroy(GCTaskThread* manager) {
|
||||||
if (manager != NULL) {
|
if (manager != NULL) {
|
||||||
delete manager;
|
delete manager;
|
||||||
|
@ -65,8 +65,6 @@ private:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
virtual void run();
|
virtual void run();
|
||||||
// Methods.
|
|
||||||
void start();
|
|
||||||
|
|
||||||
void print_task_time_stamps();
|
void print_task_time_stamps();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -130,10 +130,7 @@ uint AdaptiveSizePolicy::calc_default_active_workers(uintx total_workers,
|
||||||
uintx max_active_workers =
|
uintx max_active_workers =
|
||||||
MAX2(active_workers_by_JT, active_workers_by_heap_size);
|
MAX2(active_workers_by_JT, active_workers_by_heap_size);
|
||||||
|
|
||||||
// Limit the number of workers to the the number created,
|
new_active_workers = MIN2(max_active_workers, (uintx) total_workers);
|
||||||
// (workers()).
|
|
||||||
new_active_workers = MIN2(max_active_workers,
|
|
||||||
(uintx) total_workers);
|
|
||||||
|
|
||||||
// Increase GC workers instantly but decrease them more
|
// Increase GC workers instantly but decrease them more
|
||||||
// slowly.
|
// slowly.
|
||||||
|
@ -167,7 +164,7 @@ uint AdaptiveSizePolicy::calc_default_active_workers(uintx total_workers,
|
||||||
"Jiggled active workers too much");
|
"Jiggled active workers too much");
|
||||||
}
|
}
|
||||||
|
|
||||||
log_trace(gc, task)("GCTaskManager::calc_default_active_workers() : "
|
log_trace(gc, task)("GCTaskManager::calc_default_active_workers() : "
|
||||||
"active_workers(): " UINTX_FORMAT " new_active_workers: " UINTX_FORMAT " "
|
"active_workers(): " UINTX_FORMAT " new_active_workers: " UINTX_FORMAT " "
|
||||||
"prev_active_workers: " UINTX_FORMAT "\n"
|
"prev_active_workers: " UINTX_FORMAT "\n"
|
||||||
" active_workers_by_JT: " UINTX_FORMAT " active_workers_by_heap_size: " UINTX_FORMAT,
|
" active_workers_by_JT: " UINTX_FORMAT " active_workers_by_heap_size: " UINTX_FORMAT,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
|
77
hotspot/src/share/vm/gc/shared/workerManager.hpp
Normal file
77
hotspot/src/share/vm/gc/shared/workerManager.hpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 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_WORKERMANAGER_HPP
|
||||||
|
#define SHARE_VM_GC_SHARED_WORKERMANAGER_HPP
|
||||||
|
|
||||||
|
#include "gc/shared/adaptiveSizePolicy.hpp"
|
||||||
|
|
||||||
|
class WorkerManager : public AllStatic {
|
||||||
|
public:
|
||||||
|
// Create additional workers as needed.
|
||||||
|
// active_workers - number of workers being requested for an upcoming
|
||||||
|
// parallel task.
|
||||||
|
// total_workers - total number of workers. This is the maximum
|
||||||
|
// number possible.
|
||||||
|
// created_workers - number of workers already created. This maybe
|
||||||
|
// less than, equal to, or greater than active workers. If greater than
|
||||||
|
// or equal to active_workers, nothing is done.
|
||||||
|
// worker_type - type of thread.
|
||||||
|
// initializing - true if this is called to get the initial number of
|
||||||
|
// GC workers.
|
||||||
|
// If initializing is true, do a vm exit if the workers cannot be created.
|
||||||
|
// The initializing = true case is for JVM start up and failing to
|
||||||
|
// create all the worker at start should considered a problem so exit.
|
||||||
|
// If initializing = false, there are already some number of worker
|
||||||
|
// threads and a failure would not be optimal but should not be fatal.
|
||||||
|
template <class WorkerType>
|
||||||
|
static uint add_workers (WorkerType* holder,
|
||||||
|
uint active_workers,
|
||||||
|
uint total_workers,
|
||||||
|
uint created_workers,
|
||||||
|
os::ThreadType worker_type,
|
||||||
|
bool initializing) {
|
||||||
|
uint start = created_workers;
|
||||||
|
uint end = MIN2(active_workers, total_workers);
|
||||||
|
for (uint worker_id = start; worker_id < end; worker_id += 1) {
|
||||||
|
WorkerThread* new_worker = holder->install_worker(worker_id);
|
||||||
|
assert(new_worker != NULL, "Failed to allocate GangWorker");
|
||||||
|
if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) {
|
||||||
|
if(initializing) {
|
||||||
|
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR,
|
||||||
|
"Cannot create worker GC thread. Out of system resources.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
created_workers++;
|
||||||
|
os::start_thread(new_worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_trace(gc, task)("AdaptiveSizePolicy::add_workers() : "
|
||||||
|
"active_workers: %u created_workers: %u",
|
||||||
|
active_workers, created_workers);
|
||||||
|
|
||||||
|
return created_workers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // SHARE_VM_GC_SHARED_WORKERMANAGER_HPP
|
|
@ -25,6 +25,7 @@
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "gc/shared/gcId.hpp"
|
#include "gc/shared/gcId.hpp"
|
||||||
#include "gc/shared/workgroup.hpp"
|
#include "gc/shared/workgroup.hpp"
|
||||||
|
#include "gc/shared/workerManager.hpp"
|
||||||
#include "memory/allocation.hpp"
|
#include "memory/allocation.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "runtime/atomic.inline.hpp"
|
#include "runtime/atomic.inline.hpp"
|
||||||
|
@ -35,37 +36,45 @@
|
||||||
// Definitions of WorkGang methods.
|
// Definitions of WorkGang methods.
|
||||||
|
|
||||||
// The current implementation will exit if the allocation
|
// The current implementation will exit if the allocation
|
||||||
// of any worker fails. Still, return a boolean so that
|
// of any worker fails.
|
||||||
// a future implementation can possibly do a partial
|
void AbstractWorkGang::initialize_workers() {
|
||||||
// initialization of the workers and report such to the
|
|
||||||
// caller.
|
|
||||||
bool AbstractWorkGang::initialize_workers() {
|
|
||||||
log_develop_trace(gc, workgang)("Constructing work gang %s with %u threads", name(), total_workers());
|
log_develop_trace(gc, workgang)("Constructing work gang %s with %u threads", name(), total_workers());
|
||||||
_workers = NEW_C_HEAP_ARRAY(AbstractGangWorker*, total_workers(), mtInternal);
|
_workers = NEW_C_HEAP_ARRAY(AbstractGangWorker*, total_workers(), mtInternal);
|
||||||
if (_workers == NULL) {
|
if (_workers == NULL) {
|
||||||
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GangWorker array.");
|
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GangWorker array.");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_active_workers = ParallelGCThreads;
|
||||||
|
if (UseDynamicNumberOfGCThreads && !FLAG_IS_CMDLINE(ParallelGCThreads)) {
|
||||||
|
_active_workers = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_workers(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AbstractGangWorker* AbstractWorkGang::install_worker(uint worker_id) {
|
||||||
|
AbstractGangWorker* new_worker = allocate_worker(worker_id);
|
||||||
|
set_thread(worker_id, new_worker);
|
||||||
|
return new_worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractWorkGang::add_workers(bool initializing) {
|
||||||
|
|
||||||
os::ThreadType worker_type;
|
os::ThreadType worker_type;
|
||||||
if (are_ConcurrentGC_threads()) {
|
if (are_ConcurrentGC_threads()) {
|
||||||
worker_type = os::cgc_thread;
|
worker_type = os::cgc_thread;
|
||||||
} else {
|
} else {
|
||||||
worker_type = os::pgc_thread;
|
worker_type = os::pgc_thread;
|
||||||
}
|
}
|
||||||
for (uint worker = 0; worker < total_workers(); worker += 1) {
|
|
||||||
AbstractGangWorker* new_worker = allocate_worker(worker);
|
_created_workers = WorkerManager::add_workers(this,
|
||||||
assert(new_worker != NULL, "Failed to allocate GangWorker");
|
_active_workers,
|
||||||
_workers[worker] = new_worker;
|
_total_workers,
|
||||||
if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) {
|
_created_workers,
|
||||||
vm_exit_out_of_memory(0, OOM_MALLOC_ERROR,
|
worker_type,
|
||||||
"Cannot create worker GC thread. Out of system resources.");
|
initializing);
|
||||||
return false;
|
_active_workers = MIN2(_created_workers, _active_workers);
|
||||||
}
|
|
||||||
if (!DisableStartThread) {
|
|
||||||
os::start_thread(new_worker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractGangWorker* AbstractWorkGang::worker(uint i) const {
|
AbstractGangWorker* AbstractWorkGang::worker(uint i) const {
|
||||||
|
@ -79,7 +88,7 @@ AbstractGangWorker* AbstractWorkGang::worker(uint i) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractWorkGang::print_worker_threads_on(outputStream* st) const {
|
void AbstractWorkGang::print_worker_threads_on(outputStream* st) const {
|
||||||
uint workers = total_workers();
|
uint workers = created_workers();
|
||||||
for (uint i = 0; i < workers; i++) {
|
for (uint i = 0; i < workers; i++) {
|
||||||
worker(i)->print_on(st);
|
worker(i)->print_on(st);
|
||||||
st->cr();
|
st->cr();
|
||||||
|
@ -88,7 +97,7 @@ void AbstractWorkGang::print_worker_threads_on(outputStream* st) const {
|
||||||
|
|
||||||
void AbstractWorkGang::threads_do(ThreadClosure* tc) const {
|
void AbstractWorkGang::threads_do(ThreadClosure* tc) const {
|
||||||
assert(tc != NULL, "Null ThreadClosure");
|
assert(tc != NULL, "Null ThreadClosure");
|
||||||
uint workers = total_workers();
|
uint workers = created_workers();
|
||||||
for (uint i = 0; i < workers; i++) {
|
for (uint i = 0; i < workers; i++) {
|
||||||
tc->do_thread(worker(i));
|
tc->do_thread(worker(i));
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,8 @@ class AbstractWorkGang : public CHeapObj<mtInternal> {
|
||||||
uint _total_workers;
|
uint _total_workers;
|
||||||
// The currently active workers in this gang.
|
// The currently active workers in this gang.
|
||||||
uint _active_workers;
|
uint _active_workers;
|
||||||
|
// The count of created workers in the gang.
|
||||||
|
uint _created_workers;
|
||||||
// Printing support.
|
// Printing support.
|
||||||
const char* _name;
|
const char* _name;
|
||||||
|
|
||||||
|
@ -120,23 +122,32 @@ class AbstractWorkGang : public CHeapObj<mtInternal> {
|
||||||
const bool _are_GC_task_threads;
|
const bool _are_GC_task_threads;
|
||||||
const bool _are_ConcurrentGC_threads;
|
const bool _are_ConcurrentGC_threads;
|
||||||
|
|
||||||
|
void set_thread(uint worker_id, AbstractGangWorker* worker) {
|
||||||
|
_workers[worker_id] = worker;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbstractWorkGang(const char* name, uint workers, bool are_GC_task_threads, bool are_ConcurrentGC_threads) :
|
AbstractWorkGang(const char* name, uint workers, bool are_GC_task_threads, bool are_ConcurrentGC_threads) :
|
||||||
_name(name),
|
_name(name),
|
||||||
_total_workers(workers),
|
_total_workers(workers),
|
||||||
_active_workers(UseDynamicNumberOfGCThreads ? 1U : workers),
|
_active_workers(UseDynamicNumberOfGCThreads ? 1U : workers),
|
||||||
|
_created_workers(0),
|
||||||
_are_GC_task_threads(are_GC_task_threads),
|
_are_GC_task_threads(are_GC_task_threads),
|
||||||
_are_ConcurrentGC_threads(are_ConcurrentGC_threads)
|
_are_ConcurrentGC_threads(are_ConcurrentGC_threads)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// Initialize workers in the gang. Return true if initialization succeeded.
|
// Initialize workers in the gang. Return true if initialization succeeded.
|
||||||
bool initialize_workers();
|
void initialize_workers();
|
||||||
|
|
||||||
bool are_GC_task_threads() const { return _are_GC_task_threads; }
|
bool are_GC_task_threads() const { return _are_GC_task_threads; }
|
||||||
bool are_ConcurrentGC_threads() const { return _are_ConcurrentGC_threads; }
|
bool are_ConcurrentGC_threads() const { return _are_ConcurrentGC_threads; }
|
||||||
|
|
||||||
uint total_workers() const { return _total_workers; }
|
uint total_workers() const { return _total_workers; }
|
||||||
|
|
||||||
|
uint created_workers() const {
|
||||||
|
return _created_workers;
|
||||||
|
}
|
||||||
|
|
||||||
virtual uint active_workers() const {
|
virtual uint active_workers() const {
|
||||||
assert(_active_workers <= _total_workers,
|
assert(_active_workers <= _total_workers,
|
||||||
"_active_workers: %u > _total_workers: %u", _active_workers, _total_workers);
|
"_active_workers: %u > _total_workers: %u", _active_workers, _total_workers);
|
||||||
|
@ -144,22 +155,29 @@ class AbstractWorkGang : public CHeapObj<mtInternal> {
|
||||||
"Unless dynamic should use total workers");
|
"Unless dynamic should use total workers");
|
||||||
return _active_workers;
|
return _active_workers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_active_workers(uint v) {
|
void set_active_workers(uint v) {
|
||||||
assert(v <= _total_workers,
|
assert(v <= _total_workers,
|
||||||
"Trying to set more workers active than there are");
|
"Trying to set more workers active than there are");
|
||||||
_active_workers = MIN2(v, _total_workers);
|
_active_workers = MIN2(v, _total_workers);
|
||||||
|
add_workers(false /* exit_on_failure */);
|
||||||
assert(v != 0, "Trying to set active workers to 0");
|
assert(v != 0, "Trying to set active workers to 0");
|
||||||
_active_workers = MAX2(1U, _active_workers);
|
|
||||||
assert(UseDynamicNumberOfGCThreads || _active_workers == _total_workers,
|
assert(UseDynamicNumberOfGCThreads || _active_workers == _total_workers,
|
||||||
"Unless dynamic should use total workers");
|
"Unless dynamic should use total workers");
|
||||||
log_info(gc, task)("GC Workers: using %d out of %d", _active_workers, _total_workers);
|
log_info(gc, task)("GC Workers: using %d out of %d", _active_workers, _total_workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add GC workers as needed.
|
||||||
|
void add_workers(bool initializing);
|
||||||
|
|
||||||
// Return the Ith worker.
|
// Return the Ith worker.
|
||||||
AbstractGangWorker* worker(uint i) const;
|
AbstractGangWorker* worker(uint i) const;
|
||||||
|
|
||||||
void threads_do(ThreadClosure* tc) const;
|
void threads_do(ThreadClosure* tc) const;
|
||||||
|
|
||||||
|
// Create a GC worker and install it into the work gang.
|
||||||
|
virtual AbstractGangWorker* install_worker(uint which);
|
||||||
|
|
||||||
// Debugging.
|
// Debugging.
|
||||||
const char* name() const { return _name; }
|
const char* name() const { return _name; }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue