# HG changeset patch # User jmasa # Date 1438924050 25200 # Thu Aug 06 22:07:30 2015 -0700 # Node ID 74de93c481575b6cbd7109215bfbcbeae333fae4 # Parent edeb8dc8ff14d2d37be5690dc9802e4fe8ceb088 6858051: Create GC worker threads dynamically Reviewed-by: diff --git a/src/share/vm/gc/parallel/gcTaskManager.cpp b/src/share/vm/gc/parallel/gcTaskManager.cpp --- a/src/share/vm/gc/parallel/gcTaskManager.cpp +++ b/src/share/vm/gc/parallel/gcTaskManager.cpp @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "gc/parallel/gcTaskManager.hpp" #include "gc/parallel/gcTaskThread.hpp" -#include "gc/shared/adaptiveSizePolicy.hpp" +#include "gc/shared/adaptiveSizePolicy.inline.hpp" #include "gc/shared/gcId.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" @@ -34,6 +34,7 @@ #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.inline.hpp" +#include "runtime/os.hpp" // // GCTask @@ -372,10 +373,27 @@ GCTaskManager::GCTaskManager(uint workers) : _workers(workers), _active_workers(0), - _idle_workers(0) { + _idle_workers(0), + _created_workers(0) { 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; +} + +bool GCTaskManager::add_workers(bool initializing) { + os::ThreadType worker_type = os::pgc_thread; + return AdaptiveSizePolicy::add_workers(this, + _active_workers, + (uint) _workers, + _created_workers, + worker_type, + initializing); +} + void GCTaskManager::initialize() { if (TraceGCTaskManager) { tty->print_cr("GCTaskManager::initialize: workers: %u", workers()); @@ -394,28 +412,28 @@ // Set up worker threads. // Distribute the workers among the available processors, // 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 || - !os::distribute_processes(workers(), processor_assignment)) { + !os::distribute_processes(workers(), _processor_assignment)) { 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); - for (uint t = 0; t < workers(); t += 1) { - set_thread(t, GCTaskThread::create(this, t, processor_assignment[t])); - } + _active_workers = AdaptiveSizePolicy::initial_number_of_workers(); + add_workers(true); + Log(gc, task, thread) log; if (log.is_trace()) { ResourceMark rm; outputStream* out = log.trace_stream(); out->print("GCTaskManager::initialize: distribution:"); for (uint t = 0; t < workers(); t += 1) { - out->print(" %u", processor_assignment[t]); + out->print(" %u", _processor_assignment[t]); } out->cr(); } - FREE_C_HEAP_ARRAY(uint, processor_assignment); } reset_busy_workers(); set_unblocked(); @@ -426,9 +444,6 @@ reset_completed_tasks(); reset_barriers(); reset_emptied_queue(); - for (uint s = 0; s < workers(); s += 1) { - thread(s)->start(); - } } GCTaskManager::~GCTaskManager() { @@ -437,13 +452,17 @@ NoopGCTask::destroy(_noop_task); _noop_task = 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)); set_thread(i, NULL); } FREE_C_HEAP_ARRAY(GCTaskThread*, _thread); _thread = NULL; } + if (_processor_assignment != NULL) { + FREE_C_HEAP_ARRAY(uint, _processor_assignment); + _processor_assignment = NULL; + } if (_resource_flag != NULL) { FREE_C_HEAP_ARRAY(bool, _resource_flag); _resource_flag = NULL; @@ -470,7 +489,10 @@ "all_workers_active() is incorrect: " "active %d ParallelGCThreads %u", active_workers(), ParallelGCThreads); - log_trace(gc, task)("GCTaskManager::set_active_gang(): " + _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(): " "all_workers_active() %d workers %d " "active %d ParallelGCThreads %u", all_workers_active(), workers(), active_workers(), @@ -499,7 +521,7 @@ // is starting). Try later to release enough idle_workers // to allow the desired number of active_workers. more_inactive_workers = - workers() - active_workers() - idle_workers(); + created_workers() - active_workers() - idle_workers(); if (more_inactive_workers < 0) { int reduced_active_workers = active_workers() + more_inactive_workers; set_active_workers(reduced_active_workers); @@ -507,7 +529,7 @@ } log_trace(gc, task)("JT: %d workers %d active %d idle %d more %d", Threads::number_of_non_daemon_threads(), - workers(), + created_workers(), active_workers(), idle_workers(), more_inactive_workers); @@ -517,7 +539,7 @@ q->enqueue(IdleGCTask::create_on_c_heap()); increment_idle_workers(); } - assert(workers() == active_workers() + idle_workers(), + assert(created_workers() == active_workers() + idle_workers(), "total workers should equal active + inactive"); add_list(q); // GCTaskQueue* q was created in a ResourceArea so a @@ -555,19 +577,20 @@ void GCTaskManager::threads_do(ThreadClosure* tc) { assert(tc != NULL, "Null ThreadClosure"); - uint num_thr = workers(); + uint num_thr = created_workers(); for (uint i = 0; i < num_thr; i++) { tc->do_thread(thread(i)); } } 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"); return _thread[which]; } void GCTaskManager::set_thread(uint which, GCTaskThread* value) { + // "_add_workers" may not have been updated yet so use workers() assert(which < workers(), "index out of bounds"); assert(value != NULL, "shouldn't have null thread"); _thread[which] = value; @@ -728,7 +751,7 @@ void GCTaskManager::release_all_resources() { // 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); } } @@ -776,11 +799,12 @@ } bool GCTaskManager::resource_flag(uint which) { - assert(which < workers(), "index out of bounds"); + assert(which < created_workers(), "index out of bounds"); return _resource_flag[which]; } void GCTaskManager::set_resource_flag(uint which, bool value) { + // "_created_workers" may not have been updated yet so use workers() assert(which < workers(), "index out of bounds"); _resource_flag[which] = value; } diff --git a/src/share/vm/gc/parallel/gcTaskManager.hpp b/src/share/vm/gc/parallel/gcTaskManager.hpp --- a/src/share/vm/gc/parallel/gcTaskManager.hpp +++ b/src/share/vm/gc/parallel/gcTaskManager.hpp @@ -370,6 +370,7 @@ Monitor* _monitor; // Notification of changes. SynchronizedGCTaskQueue* _queue; // Queue of tasks. GCTaskThread** _thread; // Array of worker threads. + uint _created_workers; // Number of workers created. uint _active_workers; // Number of active workers. uint _busy_workers; // Number of busy workers. uint _blocking_worker; // The worker that's blocking. @@ -381,6 +382,8 @@ NoopGCTask* _noop_task; // The NoopGCTask instance. WaitHelper _wait_helper; // Used by inactive worker volatile uint _idle_workers; // Number of idled workers + uint* _processor_assignment; // Worker to cpu mappings. May + // be used lazily public: // Factory create and destroy methods. static GCTaskManager* create(uint workers) { @@ -546,6 +549,13 @@ uint active_workers() const { 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. + bool add_workers(bool initializing); }; // diff --git a/src/share/vm/gc/parallel/gcTaskThread.cpp b/src/share/vm/gc/parallel/gcTaskThread.cpp --- a/src/share/vm/gc/parallel/gcTaskThread.cpp +++ b/src/share/vm/gc/parallel/gcTaskThread.cpp @@ -44,9 +44,6 @@ _time_stamps(NULL), _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_name("ParGC Thread#%d", which); } diff --git a/src/share/vm/gc/parallel/gcTaskThread.hpp b/src/share/vm/gc/parallel/gcTaskThread.hpp --- a/src/share/vm/gc/parallel/gcTaskThread.hpp +++ b/src/share/vm/gc/parallel/gcTaskThread.hpp @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -48,13 +48,13 @@ bool _is_working; // True if participating in GC tasks - public: // Factory create and destroy methods. static GCTaskThread* create(GCTaskManager* manager, uint which, uint processor_id) { return new GCTaskThread(manager, which, processor_id); } + public: static void destroy(GCTaskThread* manager) { if (manager != NULL) { delete manager; diff --git a/src/share/vm/gc/shared/adaptiveSizePolicy.cpp b/src/share/vm/gc/shared/adaptiveSizePolicy.cpp --- a/src/share/vm/gc/shared/adaptiveSizePolicy.cpp +++ b/src/share/vm/gc/shared/adaptiveSizePolicy.cpp @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -130,10 +130,7 @@ uintx max_active_workers = MAX2(active_workers_by_JT, active_workers_by_heap_size); - // Limit the number of workers to the the number created, - // (workers()). - new_active_workers = MIN2(max_active_workers, - (uintx) total_workers); + new_active_workers = MIN2(max_active_workers, (uintx) total_workers); // Increase GC workers instantly but decrease them more // slowly. @@ -167,7 +164,7 @@ "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 " " "prev_active_workers: " UINTX_FORMAT "\n" " active_workers_by_JT: " UINTX_FORMAT " active_workers_by_heap_size: " UINTX_FORMAT, diff --git a/src/share/vm/gc/shared/adaptiveSizePolicy.hpp b/src/share/vm/gc/shared/adaptiveSizePolicy.hpp --- a/src/share/vm/gc/shared/adaptiveSizePolicy.hpp +++ b/src/share/vm/gc/shared/adaptiveSizePolicy.hpp @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/universe.hpp" +#include "runtime/os.hpp" // This class keeps statistical information and computes the // size of the heap. @@ -38,6 +39,8 @@ // Forward decls class elapsedTimer; class CollectorPolicy; +class AbstractWorkGang; +class GCTaskManager; class AdaptiveSizePolicy : public CHeapObj { friend class GCAdaptivePolicyCounters; @@ -343,6 +346,45 @@ double gc_pause_goal_sec, uint gc_cost_ratio); + // Returns the number of GC threads to create at JVM initialization. + // Note that the creation of additional threads is connected to the + // ergonomic decision on how many threads to use for an upcoming task. + // If the user has chosen to control the number of GC workers, they + // must all be created at initialization (since the implementation will + // never try to create more). + static uint initial_number_of_workers() { + uint initial_workers = ParallelGCThreads; + if (UseDynamicNumberOfGCThreads && FLAG_IS_DEFAULT(ParallelGCThreads)) { + initial_workers = MIN2(2U, ParallelGCThreads); + } + + return initial_workers; + } + + // Create additional workers as needed. + // dynamic_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 dynamic workers. If greater than + // or equal to dynamic_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 + inline static bool add_workers(WorkerType* holder, + uint& active_workers, + uint total_workers, + uint& created_workers, + os::ThreadType worker_type, + bool initializing); + // Return number default GC threads to use in the next GC. static uint calc_default_active_workers(uintx total_workers, const uintx min_workers, diff --git a/src/share/vm/gc/shared/adaptiveSizePolicy.inline.hpp b/src/share/vm/gc/shared/adaptiveSizePolicy.inline.hpp new file mode 100644 --- /dev/null +++ b/src/share/vm/gc/shared/adaptiveSizePolicy.inline.hpp @@ -0,0 +1,65 @@ +/* + * 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_ADAPTIVESIZEPOLICY_INLINE_HPP +#define SHARE_VM_GC_SHARED_ADAPTIVESIZEPOLICY_INLINE_HPP + +#include "gc/shared/adaptiveSizePolicy.hpp" + +template +bool AdaptiveSizePolicy::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."); + } + return false; + } + created_workers++; + + if (!DisableStartThread || !initializing) { + os::start_thread(new_worker); + } + } + + active_workers = MIN2(active_workers, created_workers); + active_workers = MAX2(1U, active_workers); + + log_trace(gc, task)("AdaptiveSizePolicy::add_workers() : " + "active_workers: %u created_workers: %u", + active_workers, created_workers); + + return true; +} +#endif // SHARE_VM_GC_SHARED_ADAPTIVESIZEPOLICY_INLINE_HPP diff --git a/src/share/vm/gc/shared/workgroup.cpp b/src/share/vm/gc/shared/workgroup.cpp --- a/src/share/vm/gc/shared/workgroup.cpp +++ b/src/share/vm/gc/shared/workgroup.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/shared/gcId.hpp" #include "gc/shared/workgroup.hpp" +#include "gc/shared/adaptiveSizePolicy.inline.hpp" #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" #include "runtime/atomic.inline.hpp" @@ -46,26 +47,33 @@ vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GangWorker array."); return false; } + + _active_workers = AdaptiveSizePolicy::initial_number_of_workers(); + return 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; +} + +bool AbstractWorkGang::add_workers(bool initializing) { + os::ThreadType worker_type; if (are_ConcurrentGC_threads()) { worker_type = os::cgc_thread; } else { worker_type = os::pgc_thread; } - for (uint worker = 0; worker < total_workers(); worker += 1) { - AbstractGangWorker* new_worker = allocate_worker(worker); - assert(new_worker != NULL, "Failed to allocate GangWorker"); - _workers[worker] = new_worker; - if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { - vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, - "Cannot create worker GC thread. Out of system resources."); - return false; - } - if (!DisableStartThread) { - os::start_thread(new_worker); - } - } - return true; + + return AdaptiveSizePolicy::add_workers(this, + _active_workers, + _total_workers, + _created_workers, + worker_type, + initializing); } AbstractGangWorker* AbstractWorkGang::worker(uint i) const { @@ -79,7 +87,7 @@ } void AbstractWorkGang::print_worker_threads_on(outputStream* st) const { - uint workers = total_workers(); + uint workers = created_workers(); for (uint i = 0; i < workers; i++) { worker(i)->print_on(st); st->cr(); @@ -88,7 +96,7 @@ void AbstractWorkGang::threads_do(ThreadClosure* tc) const { assert(tc != NULL, "Null ThreadClosure"); - uint workers = total_workers(); + uint workers = created_workers(); for (uint i = 0; i < workers; i++) { tc->do_thread(worker(i)); } diff --git a/src/share/vm/gc/shared/workgroup.hpp b/src/share/vm/gc/shared/workgroup.hpp --- a/src/share/vm/gc/shared/workgroup.hpp +++ b/src/share/vm/gc/shared/workgroup.hpp @@ -112,6 +112,8 @@ uint _total_workers; // The currently active workers in this gang. uint _active_workers; + // The count of created workers in the gang. + uint _created_workers; // Printing support. const char* _name; @@ -120,11 +122,16 @@ const bool _are_GC_task_threads; const bool _are_ConcurrentGC_threads; + void set_thread(uint worker_id, AbstractGangWorker* worker) { + _workers[worker_id] = worker; + } + public: AbstractWorkGang(const char* name, uint workers, bool are_GC_task_threads, bool are_ConcurrentGC_threads) : _name(name), _total_workers(workers), _active_workers(UseDynamicNumberOfGCThreads ? 1U : workers), + _created_workers(0), _are_GC_task_threads(are_GC_task_threads), _are_ConcurrentGC_threads(are_ConcurrentGC_threads) { } @@ -137,6 +144,10 @@ uint total_workers() const { return _total_workers; } + uint created_workers() const { + return _created_workers; + } + virtual uint active_workers() const { assert(_active_workers <= _total_workers, "_active_workers: %u > _total_workers: %u", _active_workers, _total_workers); @@ -144,22 +155,29 @@ "Unless dynamic should use total workers"); return _active_workers; } + void set_active_workers(uint v) { assert(v <= _total_workers, "Trying to set more workers active than there are"); _active_workers = MIN2(v, _total_workers); + add_workers(false /* exit_on_failure */); assert(v != 0, "Trying to set active workers to 0"); - _active_workers = MAX2(1U, _active_workers); assert(UseDynamicNumberOfGCThreads || _active_workers == _total_workers, "Unless dynamic should use total workers"); log_info(gc, task)("GC Workers: using %d out of %d", _active_workers, _total_workers); } + // Add GC workers as needed. + bool add_workers(bool initializing); + // Return the Ith worker. AbstractGangWorker* worker(uint i) 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. const char* name() const { return _name; }