6593758: RFE: Enhance GC ergonomics to dynamically choose ParallelGCThreads

Select number of GC threads dynamically based on heap usage and number of Java threads

Reviewed-by: johnc, ysr, jcoomes
This commit is contained in:
Jon Masamitsu 2011-08-09 10:16:01 -07:00
parent 098ed89645
commit 15070123fa
39 changed files with 1523 additions and 231 deletions

View file

@ -28,8 +28,10 @@
#include "memory/collectorPolicy.hpp"
#include "runtime/timer.hpp"
#include "utilities/ostream.hpp"
#include "utilities/workgroup.hpp"
elapsedTimer AdaptiveSizePolicy::_minor_timer;
elapsedTimer AdaptiveSizePolicy::_major_timer;
bool AdaptiveSizePolicy::_debug_perturbation = false;
// The throughput goal is implemented as
// _throughput_goal = 1 - ( 1 / (1 + gc_cost_ratio))
@ -88,6 +90,134 @@ AdaptiveSizePolicy::AdaptiveSizePolicy(size_t init_eden_size,
_young_gen_policy_is_ready = false;
}
// If the number of GC threads was set on the command line,
// use it.
// Else
// Calculate the number of GC threads based on the number of Java threads.
// Calculate the number of GC threads based on the size of the heap.
// Use the larger.
int AdaptiveSizePolicy::calc_default_active_workers(uintx total_workers,
const uintx min_workers,
uintx active_workers,
uintx application_workers) {
// If the user has specifically set the number of
// GC threads, use them.
// If the user has turned off using a dynamic number of GC threads
// or the users has requested a specific number, set the active
// number of workers to all the workers.
uintx new_active_workers = total_workers;
uintx prev_active_workers = active_workers;
uintx active_workers_by_JT = 0;
uintx active_workers_by_heap_size = 0;
// Always use at least min_workers but use up to
// GCThreadsPerJavaThreads * application threads.
active_workers_by_JT =
MAX2((uintx) GCWorkersPerJavaThread * application_workers,
min_workers);
// Choose a number of GC threads based on the current size
// of the heap. This may be complicated because the size of
// the heap depends on factors such as the thoughput goal.
// Still a large heap should be collected by more GC threads.
active_workers_by_heap_size =
MAX2((size_t) 2U, Universe::heap()->capacity() / HeapSizePerGCThread);
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);
// Increase GC workers instantly but decrease them more
// slowly.
if (new_active_workers < prev_active_workers) {
new_active_workers =
MAX2(min_workers, (prev_active_workers + new_active_workers) / 2);
}
// Check once more that the number of workers is within the limits.
assert(min_workers <= total_workers, "Minimum workers not consistent with total workers");
assert(new_active_workers >= min_workers, "Minimum workers not observed");
assert(new_active_workers <= total_workers, "Total workers not observed");
if (ForceDynamicNumberOfGCThreads) {
// Assume this is debugging and jiggle the number of GC threads.
if (new_active_workers == prev_active_workers) {
if (new_active_workers < total_workers) {
new_active_workers++;
} else if (new_active_workers > min_workers) {
new_active_workers--;
}
}
if (new_active_workers == total_workers) {
if (_debug_perturbation) {
new_active_workers = min_workers;
}
_debug_perturbation = !_debug_perturbation;
}
assert((new_active_workers <= (uintx) ParallelGCThreads) &&
(new_active_workers >= min_workers),
"Jiggled active workers too much");
}
if (TraceDynamicGCThreads) {
gclog_or_tty->print_cr("GCTaskManager::calc_default_active_workers() : "
"active_workers(): %d new_acitve_workers: %d "
"prev_active_workers: %d\n"
" active_workers_by_JT: %d active_workers_by_heap_size: %d",
active_workers, new_active_workers, prev_active_workers,
active_workers_by_JT, active_workers_by_heap_size);
}
assert(new_active_workers > 0, "Always need at least 1");
return new_active_workers;
}
int AdaptiveSizePolicy::calc_active_workers(uintx total_workers,
uintx active_workers,
uintx application_workers) {
// If the user has specifically set the number of
// GC threads, use them.
// If the user has turned off using a dynamic number of GC threads
// or the users has requested a specific number, set the active
// number of workers to all the workers.
int new_active_workers;
if (!UseDynamicNumberOfGCThreads ||
(!FLAG_IS_DEFAULT(ParallelGCThreads) && !ForceDynamicNumberOfGCThreads)) {
new_active_workers = total_workers;
} else {
new_active_workers = calc_default_active_workers(total_workers,
2, /* Minimum number of workers */
active_workers,
application_workers);
}
assert(new_active_workers > 0, "Always need at least 1");
return new_active_workers;
}
int AdaptiveSizePolicy::calc_active_conc_workers(uintx total_workers,
uintx active_workers,
uintx application_workers) {
if (!UseDynamicNumberOfGCThreads ||
(!FLAG_IS_DEFAULT(ConcGCThreads) && !ForceDynamicNumberOfGCThreads)) {
return ConcGCThreads;
} else {
int no_of_gc_threads = calc_default_active_workers(
total_workers,
1, /* Minimum number of workers */
active_workers,
application_workers);
return no_of_gc_threads;
}
}
bool AdaptiveSizePolicy::tenuring_threshold_change() const {
return decrement_tenuring_threshold_for_gc_cost() ||
increment_tenuring_threshold_for_gc_cost() ||

View file

@ -187,6 +187,8 @@ class AdaptiveSizePolicy : public CHeapObj {
julong _young_gen_change_for_minor_throughput;
julong _old_gen_change_for_major_throughput;
static const uint GCWorkersPerJavaThread = 2;
// Accessors
double gc_pause_goal_sec() const { return _gc_pause_goal_sec; }
@ -331,6 +333,8 @@ class AdaptiveSizePolicy : public CHeapObj {
// Return true if the policy suggested a change.
bool tenuring_threshold_change() const;
static bool _debug_perturbation;
public:
AdaptiveSizePolicy(size_t init_eden_size,
size_t init_promo_size,
@ -338,6 +342,31 @@ class AdaptiveSizePolicy : public CHeapObj {
double gc_pause_goal_sec,
uint gc_cost_ratio);
// Return number default GC threads to use in the next GC.
static int calc_default_active_workers(uintx total_workers,
const uintx min_workers,
uintx active_workers,
uintx application_workers);
// Return number of GC threads to use in the next GC.
// This is called sparingly so as not to change the
// number of GC workers gratuitously.
// For ParNew collections
// For PS scavenge and ParOld collections
// For G1 evacuation pauses (subject to update)
// Other collection phases inherit the number of
// GC workers from the calls above. For example,
// a CMS parallel remark uses the same number of GC
// workers as the most recent ParNew collection.
static int calc_active_workers(uintx total_workers,
uintx active_workers,
uintx application_workers);
// Return number of GC threads to use in the next concurrent GC phase.
static int calc_active_conc_workers(uintx total_workers,
uintx active_workers,
uintx application_workers);
bool is_gc_cms_adaptive_size_policy() {
return kind() == _gc_cms_adaptive_size_policy;
}