Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8268372: ZGC: dynamically select the number of concurrent GC threads used #4410

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -72,6 +72,12 @@ void ZArguments::initialize() {
vm_exit_during_initialization("The flag -XX:+UseZGC can not be combined with -XX:ConcGCThreads=0");
}

// The heuristics used when UseDynamicNumberOfGCThreads is
// enabled defaults to using a ZAllocationSpikeTolerance of 1.
if (UseDynamicNumberOfGCThreads && FLAG_IS_DEFAULT(ZAllocationSpikeTolerance)) {
FLAG_SET_DEFAULT(ZAllocationSpikeTolerance, 1);
}

#ifdef COMPILER2
// Enable loop strip mining by default
if (FLAG_IS_DEFAULT(UseCountedLoopSafepoints)) {
@@ -50,8 +50,8 @@ ZCollectedHeap::ZCollectedHeap() :
_barrier_set(),
_initialize(&_barrier_set),
_heap(),
_director(new ZDirector()),
_driver(new ZDriver()),
_director(new ZDirector(_driver)),
_stat(new ZStat()),
_runtime_workers() {}

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -44,8 +44,8 @@ class ZCollectedHeap : public CollectedHeap {
ZBarrierSet _barrier_set;
ZInitialize _initialize;
ZHeap _heap;
ZDirector* _director;
ZDriver* _driver;
ZDirector* _director;
ZStat* _stat;
ZRuntimeWorkers _runtime_workers;

Large diffs are not rendered by default.

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,31 +25,21 @@
#define SHARE_GC_Z_ZDIRECTOR_HPP

#include "gc/shared/concurrentGCThread.hpp"
#include "gc/shared/gcCause.hpp"
#include "gc/z/zMetronome.hpp"

class ZDriver;

class ZDirector : public ConcurrentGCThread {
private:
static const double one_in_1000;

const size_t _relocation_headroom;
ZMetronome _metronome;

void sample_allocation_rate() const;

bool rule_timer() const;
bool rule_warmup() const;
bool rule_allocation_rate() const;
bool rule_proactive() const;
bool rule_high_usage() const;
GCCause::Cause make_gc_decision() const;
ZDriver* const _driver;
ZMetronome _metronome;

protected:
virtual void run_service();
virtual void stop_service();

public:
ZDirector();
ZDirector(ZDriver* driver);
};

#endif // SHARE_GC_Z_ZDIRECTOR_HPP
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,28 @@ static const ZStatPhaseConcurrent ZPhaseConcurrentRelocated("Concurrent Relocate
static const ZStatCriticalPhase ZCriticalPhaseGCLockerStall("GC Locker Stall", false /* verbose */);
static const ZStatSampler ZSamplerJavaThreads("System", "Java Threads", ZStatUnitThreads);

ZDriverRequest::ZDriverRequest() :
ZDriverRequest(GCCause::_no_gc) {}

ZDriverRequest::ZDriverRequest(GCCause::Cause cause) :
ZDriverRequest(cause, ConcGCThreads) {}

ZDriverRequest::ZDriverRequest(GCCause::Cause cause, uint nworkers) :
_cause(cause),
_nworkers(nworkers) {}

bool ZDriverRequest::operator==(const ZDriverRequest& other) const {
return _cause == other._cause;
}

GCCause::Cause ZDriverRequest::cause() const {
return _cause;
}

uint ZDriverRequest::nworkers() const {
return _nworkers;
}

class VM_ZOperation : public VM_Operation {
private:
const uint _gc_id;
@@ -118,47 +140,6 @@ class VM_ZOperation : public VM_Operation {
}
};

static bool should_clear_soft_references() {
// Clear if one or more allocations have stalled
const bool stalled = ZHeap::heap()->is_alloc_stalled();
if (stalled) {
// Clear
return true;
}

// Clear if implied by the GC cause
const GCCause::Cause cause = ZCollectedHeap::heap()->gc_cause();
if (cause == GCCause::_wb_full_gc ||
cause == GCCause::_metadata_GC_clear_soft_refs) {
// Clear
return true;
}

// Don't clear
return false;
}

static bool should_boost_worker_threads() {
// Boost worker threads if one or more allocations have stalled
const bool stalled = ZHeap::heap()->is_alloc_stalled();
if (stalled) {
// Boost
return true;
}

// Boost worker threads if implied by the GC cause
const GCCause::Cause cause = ZCollectedHeap::heap()->gc_cause();
if (cause == GCCause::_wb_full_gc ||
cause == GCCause::_java_lang_system_gc ||
cause == GCCause::_metadata_GC_clear_soft_refs) {
// Boost
return true;
}

// Don't boost
return false;
}

class VM_ZMarkStart : public VM_ZOperation {
public:
virtual VMOp_Type type() const {
@@ -173,14 +154,6 @@ class VM_ZMarkStart : public VM_ZOperation {
ZStatTimer timer(ZPhasePauseMarkStart);
ZServiceabilityPauseTracer tracer;

// Set up soft reference policy
const bool clear = should_clear_soft_references();
ZHeap::heap()->set_soft_reference_policy(clear);

// Set up boost mode
const bool boost = should_boost_worker_threads();
ZHeap::heap()->set_boost_worker_threads(boost);

ZCollectedHeap::heap()->increment_total_collections(true /* full */);

ZHeap::heap()->mark_start();
@@ -241,8 +214,12 @@ ZDriver::ZDriver() :
create_and_start();
}

void ZDriver::collect(GCCause::Cause cause) {
switch (cause) {
bool ZDriver::is_busy() const {
return _gc_cycle_port.is_busy();
}

void ZDriver::collect(const ZDriverRequest& request) {
switch (request.cause()) {
case GCCause::_wb_young_gc:
case GCCause::_wb_conc_mark:
case GCCause::_wb_full_gc:
@@ -253,7 +230,7 @@ void ZDriver::collect(GCCause::Cause cause) {
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
// Start synchronous GC
_gc_cycle_port.send_sync(cause);
_gc_cycle_port.send_sync(request);
break;

case GCCause::_z_timer:
@@ -264,7 +241,7 @@ void ZDriver::collect(GCCause::Cause cause) {
case GCCause::_z_high_usage:
case GCCause::_metadata_GC_threshold:
// Start asynchronous GC
_gc_cycle_port.send_async(cause);
_gc_cycle_port.send_async(request);
break;

case GCCause::_gc_locker:
@@ -274,12 +251,12 @@ void ZDriver::collect(GCCause::Cause cause) {

case GCCause::_wb_breakpoint:
ZBreakpoint::start_gc();
_gc_cycle_port.send_async(cause);
_gc_cycle_port.send_async(request);
break;

default:
// Other causes not supported
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
fatal("Unsupported GC cause (%s)", GCCause::to_string(request.cause()));
break;
}
}
@@ -369,6 +346,50 @@ void ZDriver::check_out_of_memory() {
ZHeap::heap()->check_out_of_memory();
}

static bool should_clear_soft_references(const ZDriverRequest& request) {
// Clear soft references if implied by the GC cause
if (request.cause() == GCCause::_wb_full_gc ||
request.cause() == GCCause::_metadata_GC_clear_soft_refs ||
request.cause() == GCCause::_z_allocation_stall) {
// Clear
return true;
}

// Don't clear
return false;
}

static uint select_active_worker_threads_dynamic(const ZDriverRequest& request) {
// Use requested number of worker threads
return request.nworkers();
}

static uint select_active_worker_threads_static(const ZDriverRequest& request) {
const GCCause::Cause cause = request.cause();
const uint nworkers = request.nworkers();

// Boost number of worker threads if implied by the GC cause
if (cause == GCCause::_wb_full_gc ||
cause == GCCause::_java_lang_system_gc ||
cause == GCCause::_metadata_GC_clear_soft_refs ||
cause == GCCause::_z_allocation_stall) {
// Boost
const uint boosted_nworkers = MAX2(nworkers, ParallelGCThreads);
return boosted_nworkers;
}

// Use requested number of worker threads
return nworkers;
}

static uint select_active_worker_threads(const ZDriverRequest& request) {
if (UseDynamicNumberOfGCThreads) {
return select_active_worker_threads_dynamic(request);
} else {
return select_active_worker_threads_static(request);
}
}

class ZDriverGCScope : public StackObj {
private:
GCIdMark _gc_id;
@@ -378,23 +399,27 @@ class ZDriverGCScope : public StackObj {
ZServiceabilityCycleTracer _tracer;

public:
ZDriverGCScope(GCCause::Cause cause) :
ZDriverGCScope(const ZDriverRequest& request) :
_gc_id(),
_gc_cause(cause),
_gc_cause_setter(ZCollectedHeap::heap(), cause),
_gc_cause(request.cause()),
_gc_cause_setter(ZCollectedHeap::heap(), _gc_cause),
_timer(ZPhaseCycle),
_tracer() {
// Update statistics
ZStatCycle::at_start();

// Set up soft reference policy
const bool clear = should_clear_soft_references(request);
ZHeap::heap()->set_soft_reference_policy(clear);

// Select number of worker threads to use
const uint nworkers = select_active_worker_threads(request);
ZHeap::heap()->set_active_workers(nworkers);
}

~ZDriverGCScope() {
// Calculate boost factor
const double boost_factor = (double)ZHeap::heap()->nconcurrent_worker_threads() /
(double)ZHeap::heap()->nconcurrent_no_boost_worker_threads();

// Update statistics
ZStatCycle::at_end(_gc_cause, boost_factor);
ZStatCycle::at_end(_gc_cause, ZHeap::heap()->active_workers());

// Update data used by soft reference policy
Universe::heap()->update_capacity_and_used_at_gc();
@@ -417,8 +442,8 @@ class ZDriverGCScope : public StackObj {
} \
} while (false)

void ZDriver::gc(GCCause::Cause cause) {
ZDriverGCScope scope(cause);
void ZDriver::gc(const ZDriverRequest& request) {
ZDriverGCScope scope(request);

// Phase 1: Pause Mark Start
pause_mark_start();
@@ -458,15 +483,15 @@ void ZDriver::run_service() {
// Main loop
while (!should_terminate()) {
// Wait for GC request
const GCCause::Cause cause = _gc_cycle_port.receive();
if (cause == GCCause::_no_gc) {
const ZDriverRequest request = _gc_cycle_port.receive();
if (request.cause() == GCCause::_no_gc) {
continue;
}

ZBreakpoint::at_before_gc();

// Run GC
gc(cause);
gc(request);

// Notify GC completed
_gc_cycle_port.ack();