blob: f0da73a7f6cc668741b37a5527aab94859343ab3 [file] [log] [blame]
/*
* Copyright (c) 2015, 2023, 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/gc_globals.hpp"
#include "gc/shared/gcCause.hpp"
#include "gc/shared/gcId.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zBreakpoint.hpp"
#include "gc/z/zCollectedHeap.hpp"
#include "gc/z/zDirector.hpp"
#include "gc/z/zDriver.hpp"
#include "gc/z/zGCIdPrinter.hpp"
#include "gc/z/zGeneration.inline.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zLock.inline.hpp"
#include "gc/z/zServiceability.hpp"
#include "gc/z/zStat.hpp"
static const ZStatPhaseCollection ZPhaseCollectionMinor("Minor Collection", true /* minor */);
static const ZStatPhaseCollection ZPhaseCollectionMajor("Major Collection", false /* minor */);
template <typename DriverT>
class ZGCCauseSetter : public GCCauseSetter {
private:
DriverT* _driver;
public:
ZGCCauseSetter(DriverT* driver, GCCause::Cause cause)
: GCCauseSetter(ZCollectedHeap::heap(), cause),
_driver(driver) {
_driver->set_gc_cause(cause);
}
~ZGCCauseSetter() {
_driver->set_gc_cause(GCCause::_no_gc);
}
};
ZLock* ZDriver::_lock;
ZDriverMinor* ZDriver::_minor;
ZDriverMajor* ZDriver::_major;
void ZDriver::initialize() {
_lock = new ZLock();
}
void ZDriver::lock() {
_lock->lock();
}
void ZDriver::unlock() {
_lock->unlock();
}
void ZDriver::set_minor(ZDriverMinor* minor) {
_minor = minor;
}
void ZDriver::set_major(ZDriverMajor* major) {
_major = major;
}
ZDriverMinor* ZDriver::minor() {
return _minor;
}
ZDriverMajor* ZDriver::major() {
return _major;
}
ZDriverLocker::ZDriverLocker() {
ZDriver::lock();
}
ZDriverLocker::~ZDriverLocker() {
ZDriver::unlock();
}
ZDriverUnlocker::ZDriverUnlocker() {
ZDriver::unlock();
}
ZDriverUnlocker::~ZDriverUnlocker() {
ZDriver::lock();
}
ZDriver::ZDriver()
: _gc_cause(GCCause::_no_gc) {}
void ZDriver::set_gc_cause(GCCause::Cause cause) {
_gc_cause = cause;
}
GCCause::Cause ZDriver::gc_cause() {
return _gc_cause;
}
ZDriverMinor::ZDriverMinor()
: ZDriver(),
_port(),
_gc_timer(),
_jfr_tracer(),
_used_at_start() {
ZDriver::set_minor(this);
set_name("ZDriverMinor");
create_and_start();
}
bool ZDriverMinor::is_busy() const {
return _port.is_busy();
}
void ZDriverMinor::collect(const ZDriverRequest& request) {
switch (request.cause()) {
case GCCause::_wb_young_gc:
// Start synchronous GC
_port.send_sync(request);
break;
case GCCause::_scavenge_alot:
case GCCause::_z_timer:
case GCCause::_z_allocation_rate:
case GCCause::_z_allocation_stall:
case GCCause::_z_high_usage:
// Start asynchronous GC
_port.send_async(request);
break;
default:
fatal("Unsupported GC cause (%s)", GCCause::to_string(request.cause()));
break;
}
};
GCTracer* ZDriverMinor::jfr_tracer() {
return &_jfr_tracer;
}
void ZDriverMinor::set_used_at_start(size_t used) {
_used_at_start = used;
}
size_t ZDriverMinor::used_at_start() const {
return _used_at_start;
}
class ZDriverScopeMinor : public StackObj {
private:
GCIdMark _gc_id;
GCCause::Cause _gc_cause;
ZGCCauseSetter<ZDriverMinor> _gc_cause_setter;
ZStatTimer _stat_timer;
ZServiceabilityCycleTracer _tracer;
public:
ZDriverScopeMinor(const ZDriverRequest& request, ConcurrentGCTimer* gc_timer)
: _gc_id(),
_gc_cause(request.cause()),
_gc_cause_setter(ZDriver::minor(), _gc_cause),
_stat_timer(ZPhaseCollectionMinor, gc_timer),
_tracer(true /* minor */) {
// Select number of worker threads to use
ZGeneration::young()->set_active_workers(request.young_nworkers());
}
};
void ZDriverMinor::gc(const ZDriverRequest& request) {
ZDriverScopeMinor scope(request, &_gc_timer);
ZGCIdMinor minor_id(gc_id());
ZGeneration::young()->collect(ZYoungType::minor, &_gc_timer);
}
static void handle_alloc_stalling_for_young() {
ZHeap::heap()->handle_alloc_stalling_for_young();
}
void ZDriverMinor::handle_alloc_stalls() const {
handle_alloc_stalling_for_young();
}
void ZDriverMinor::run_thread() {
// Main loop
for (;;) {
// Wait for GC request
const ZDriverRequest request = _port.receive();
ZDriverLocker locker;
abortpoint();
// Run GC
gc(request);
abortpoint();
// Notify GC completed
_port.ack();
// Handle allocation stalls
handle_alloc_stalls();
// Good point to consider back-to-back GC
ZDirector::evaluate_rules();
}
}
void ZDriverMinor::terminate() {
const ZDriverRequest request(GCCause::_no_gc, 0, 0);
_port.send_async(request);
}
static bool should_clear_soft_references(GCCause::Cause cause) {
// Clear soft references if implied by the GC cause
switch (cause) {
case GCCause::_wb_full_gc:
case GCCause::_metadata_GC_clear_soft_refs:
case GCCause::_z_allocation_stall:
return true;
case GCCause::_heap_dump:
case GCCause::_heap_inspection:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_proactive:
case GCCause::_metadata_GC_threshold:
case GCCause::_codecache_GC_threshold:
case GCCause::_codecache_GC_aggressive:
break;
default:
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
break;
}
// Clear soft references if threads are stalled waiting for an old collection
if (ZHeap::heap()->is_alloc_stalling_for_old()) {
return true;
}
// Don't clear
return false;
}
static bool should_preclean_young(GCCause::Cause cause) {
// Preclean young if implied by the GC cause
switch (cause) {
case GCCause::_heap_dump:
case GCCause::_heap_inspection:
case GCCause::_wb_full_gc:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
case GCCause::_z_allocation_stall:
return true;
case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_proactive:
case GCCause::_metadata_GC_threshold:
case GCCause::_codecache_GC_threshold:
case GCCause::_codecache_GC_aggressive:
break;
default:
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
break;
}
// Preclean young if threads are stalled waiting for an old collection
if (ZHeap::heap()->is_alloc_stalling_for_old()) {
return true;
}
// It is important that when soft references are cleared, we also pre-clean the young
// generation, as we might otherwise throw premature OOM. Therefore, all causes that
// trigger soft ref cleaning must also trigger pre-cleaning of young gen. If allocations
// stalled when checking for soft ref cleaning, then since we hold the driver locker all
// the way until we check for young gen pre-cleaning, we can be certain that we should
// catch that above and perform young gen pre-cleaning.
assert(!should_clear_soft_references(cause), "Clearing soft references without pre-cleaning young gen");
// Preclean young if implied by configuration
return ScavengeBeforeFullGC;
}
ZDriverMajor::ZDriverMajor()
: ZDriver(),
_port(),
_gc_timer(),
_jfr_tracer(),
_used_at_start() {
ZDriver::set_major(this);
set_name("ZDriverMajor");
create_and_start();
}
bool ZDriverMajor::is_busy() const {
return _port.is_busy();
}
void ZDriverMajor::collect(const ZDriverRequest& request) {
switch (request.cause()) {
case GCCause::_heap_dump:
case GCCause::_heap_inspection:
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
case GCCause::_codecache_GC_aggressive:
// Start synchronous GC
_port.send_sync(request);
break;
case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_allocation_stall:
case GCCause::_z_proactive:
case GCCause::_codecache_GC_threshold:
case GCCause::_metadata_GC_threshold:
// Start asynchronous GC
_port.send_async(request);
break;
case GCCause::_wb_breakpoint:
ZBreakpoint::start_gc();
_port.send_async(request);
break;
default:
fatal("Unsupported GC cause (%s)", GCCause::to_string(request.cause()));
break;
}
}
GCTracer* ZDriverMajor::jfr_tracer() {
return &_jfr_tracer;
}
void ZDriverMajor::set_used_at_start(size_t used) {
_used_at_start = used;
}
size_t ZDriverMajor::used_at_start() const {
return _used_at_start;
}
class ZDriverScopeMajor : public StackObj {
private:
GCIdMark _gc_id;
GCCause::Cause _gc_cause;
ZGCCauseSetter<ZDriverMajor> _gc_cause_setter;
ZStatTimer _stat_timer;
ZServiceabilityCycleTracer _tracer;
public:
ZDriverScopeMajor(const ZDriverRequest& request, ConcurrentGCTimer* gc_timer)
: _gc_id(),
_gc_cause(request.cause()),
_gc_cause_setter(ZDriver::major(), _gc_cause),
_stat_timer(ZPhaseCollectionMajor, gc_timer),
_tracer(false /* minor */) {
// Select number of worker threads to use
ZGeneration::young()->set_active_workers(request.young_nworkers());
ZGeneration::old()->set_active_workers(request.old_nworkers());
}
~ZDriverScopeMajor() {
// Update data used by soft reference policy
ZCollectedHeap::heap()->update_capacity_and_used_at_gc();
// Signal that we have completed a visit to all live objects
ZCollectedHeap::heap()->record_whole_heap_examined_timestamp();
}
};
void ZDriverMajor::collect_young(const ZDriverRequest& request) {
ZGCIdMajor major_id(gc_id(), 'Y');
if (should_preclean_young(request.cause())) {
// Collect young generation and promote everything to old generation
ZGeneration::young()->collect(ZYoungType::major_full_preclean, &_gc_timer);
abortpoint();
// Collect young generation and gather roots pointing into old generation
ZGeneration::young()->collect(ZYoungType::major_full_roots, &_gc_timer);
} else {
// Collect young generation and gather roots pointing into old generation
ZGeneration::young()->collect(ZYoungType::major_partial_roots, &_gc_timer);
}
abortpoint();
// Handle allocations waiting for a young collection
handle_alloc_stalling_for_young();
}
void ZDriverMajor::collect_old() {
ZGCIdMajor major_id(gc_id(), 'O');
ZGeneration::old()->collect(&_gc_timer);
}
void ZDriverMajor::gc(const ZDriverRequest& request) {
ZDriverScopeMajor scope(request, &_gc_timer);
// Collect the young generation
collect_young(request);
abortpoint();
// Collect the old generation
collect_old();
}
static void handle_alloc_stalling_for_old(bool cleared_soft_refs) {
ZHeap::heap()->handle_alloc_stalling_for_old(cleared_soft_refs);
}
void ZDriverMajor::handle_alloc_stalls(bool cleared_soft_refs) const {
handle_alloc_stalling_for_old(cleared_soft_refs);
}
void ZDriverMajor::run_thread() {
// Main loop
for (;;) {
// Wait for GC request
const ZDriverRequest request = _port.receive();
ZDriverLocker locker;
ZBreakpoint::at_before_gc();
abortpoint();
// Set up soft reference policy
const bool clear_soft_refs = should_clear_soft_references(request.cause());
ZGeneration::old()->set_soft_reference_policy(clear_soft_refs);
// Run GC
gc(request);
abortpoint();
// Notify GC completed
_port.ack();
// Handle allocation stalls
handle_alloc_stalls(clear_soft_refs);
ZBreakpoint::at_after_gc();
}
}
void ZDriverMajor::terminate() {
const ZDriverRequest request(GCCause::_no_gc, 0, 0);
_port.send_async(request);
}