8332841: GenShen: Pull shared members from control thread into common base class

Reviewed-by: ysr
This commit is contained in:
William Kemper 2024-05-24 18:10:31 +00:00
parent 236432dbdb
commit ebc520e83f
5 changed files with 236 additions and 141 deletions

@ -35,25 +35,21 @@
#include "gc/shenandoah/shenandoahPacer.inline.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
#include "gc/shenandoah/mode/shenandoahMode.hpp"
#include "logging/log.hpp"
#include "memory/metaspaceUtils.hpp"
#include "memory/metaspaceStats.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/atomic.hpp"
ShenandoahControlThread::ShenandoahControlThread() :
ConcurrentGCThread(),
_alloc_failure_waiters_lock(Mutex::safepoint-2, "ShenandoahAllocFailureGC_lock", true),
_gc_waiters_lock(Mutex::safepoint-2, "ShenandoahRequestedGC_lock", true),
ShenandoahController(),
_requested_gc_cause(GCCause::_no_cause_specified),
_degen_point(ShenandoahGC::_degenerated_outside_cycle),
_allocs_seen(0) {
_degen_point(ShenandoahGC::_degenerated_outside_cycle) {
set_name("Shenandoah Control Thread");
reset_gc_id();
create_and_start();
}
void ShenandoahControlThread::run_service() {
ShenandoahHeap* heap = ShenandoahHeap::heap();
ShenandoahHeap* const heap = ShenandoahHeap::heap();
const GCMode default_mode = concurrent_normal;
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
@ -77,7 +73,7 @@ void ShenandoahControlThread::run_service() {
const GCCause::Cause requested_gc_cause = _requested_gc_cause;
// This control loop iteration has seen this much allocation.
const size_t allocs_seen = Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed);
const size_t allocs_seen = reset_allocs_seen();
// Check if we have seen a new target for soft max heap size.
const bool soft_max_changed = heap->check_soft_max_changed();
@ -106,7 +102,6 @@ void ShenandoahControlThread::run_service() {
policy->record_alloc_failure_to_full();
mode = stw_full;
}
} else if (is_gc_requested) {
cause = requested_gc_cause;
log_info(gc)("Trigger: GC request (%s)", GCCause::to_string(cause));
@ -239,7 +234,7 @@ void ShenandoahControlThread::run_service() {
heap->pacer()->setup_for_idle();
}
} else {
// Allow allocators to know we have seen this much regions
// Report to pacer that we have seen this many words allocated
if (ShenandoahPacing && (allocs_seen > 0)) {
heap->pacer()->report_alloc(allocs_seen);
}
@ -407,88 +402,8 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
}
}
void ShenandoahControlThread::handle_alloc_failure(ShenandoahAllocRequest& req, bool block) {
ShenandoahHeap* heap = ShenandoahHeap::heap();
assert(current()->is_Java_thread(), "expect Java thread here");
if (try_set_alloc_failure_gc()) {
// Only report the first allocation failure
log_info(gc)("Failed to allocate %s, " SIZE_FORMAT "%s",
req.type_string(),
byte_size_in_proper_unit(req.size() * HeapWordSize), proper_unit_for_byte_size(req.size() * HeapWordSize));
// Now that alloc failure GC is scheduled, we can abort everything else
heap->cancel_gc(GCCause::_allocation_failure);
}
if (block) {
MonitorLocker ml(&_alloc_failure_waiters_lock);
while (is_alloc_failure_gc()) {
ml.wait();
}
}
}
void ShenandoahControlThread::handle_alloc_failure_evac(size_t words) {
ShenandoahHeap* heap = ShenandoahHeap::heap();
if (try_set_alloc_failure_gc()) {
// Only report the first allocation failure
log_info(gc)("Failed to allocate " SIZE_FORMAT "%s for evacuation",
byte_size_in_proper_unit(words * HeapWordSize), proper_unit_for_byte_size(words * HeapWordSize));
}
// Forcefully report allocation failure
heap->cancel_gc(GCCause::_shenandoah_allocation_failure_evac);
}
void ShenandoahControlThread::notify_alloc_failure_waiters() {
_alloc_failure_gc.unset();
MonitorLocker ml(&_alloc_failure_waiters_lock);
ml.notify_all();
}
bool ShenandoahControlThread::try_set_alloc_failure_gc() {
return _alloc_failure_gc.try_set();
}
bool ShenandoahControlThread::is_alloc_failure_gc() {
return _alloc_failure_gc.is_set();
}
void ShenandoahControlThread::notify_gc_waiters() {
_gc_requested.unset();
MonitorLocker ml(&_gc_waiters_lock);
ml.notify_all();
}
void ShenandoahControlThread::pacing_notify_alloc(size_t words) {
assert(ShenandoahPacing, "should only call when pacing is enabled");
Atomic::add(&_allocs_seen, words, memory_order_relaxed);
}
void ShenandoahControlThread::reset_gc_id() {
Atomic::store(&_gc_id, (size_t)0);
}
void ShenandoahControlThread::update_gc_id() {
Atomic::inc(&_gc_id);
}
size_t ShenandoahControlThread::get_gc_id() {
return Atomic::load(&_gc_id);
}
void ShenandoahControlThread::start() {
create_and_start();
}
void ShenandoahControlThread::prepare_for_graceful_shutdown() {
_graceful_shutdown.set();
}
bool ShenandoahControlThread::in_graceful_shutdown() {
return _graceful_shutdown.is_set();
}

@ -28,10 +28,11 @@
#include "gc/shared/gcCause.hpp"
#include "gc/shared/concurrentGCThread.hpp"
#include "gc/shenandoah/shenandoahGC.hpp"
#include "gc/shenandoah/shenandoahController.hpp"
#include "gc/shenandoah/shenandoahPadding.hpp"
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
class ShenandoahControlThread: public ConcurrentGCThread {
class ShenandoahControlThread: public ShenandoahController {
friend class VMStructs;
private:
@ -42,68 +43,30 @@ private:
stw_full
} GCMode;
// While we could have a single lock for these, it may risk unblocking
// GC waiters when alloc failure GC cycle finishes. We want instead
// to make complete explicit cycle for for demanding customers.
Monitor _alloc_failure_waiters_lock;
Monitor _gc_waiters_lock;
public:
void run_service();
void stop_service();
private:
ShenandoahSharedFlag _gc_requested;
ShenandoahSharedFlag _alloc_failure_gc;
ShenandoahSharedFlag _graceful_shutdown;
GCCause::Cause _requested_gc_cause;
ShenandoahGC::ShenandoahDegenPoint _degen_point;
shenandoah_padding(0);
volatile size_t _allocs_seen;
shenandoah_padding(1);
volatile size_t _gc_id;
shenandoah_padding(2);
public:
ShenandoahControlThread();
void run_service() override;
void stop_service() override;
void request_gc(GCCause::Cause cause) override;
private:
bool check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point);
void service_concurrent_normal_cycle(GCCause::Cause cause);
void service_stw_full_cycle(GCCause::Cause cause);
void service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point);
bool try_set_alloc_failure_gc();
void notify_alloc_failure_waiters();
bool is_alloc_failure_gc();
void reset_gc_id();
void update_gc_id();
size_t get_gc_id();
void notify_gc_waiters();
// Handle GC request.
// Blocks until GC is over.
void handle_requested_gc(GCCause::Cause cause);
public:
// Constructor
ShenandoahControlThread();
// Handle allocation failure from a mutator allocation.
// Optionally blocks while collector is handling the failure. If the GC
// threshold has been exceeded, the mutator allocation will not block so
// that the out of memory error can be raised promptly.
void handle_alloc_failure(ShenandoahAllocRequest& req, bool block = true);
// Handle allocation failure from evacuation path.
void handle_alloc_failure_evac(size_t words);
void request_gc(GCCause::Cause cause);
void pacing_notify_alloc(size_t words);
void start();
void prepare_for_graceful_shutdown();
bool in_graceful_shutdown();
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLTHREAD_HPP

@ -0,0 +1,112 @@
/*
* Copyright Amazon.com Inc. 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/shenandoah/shenandoahController.hpp"
#include "gc/shenandoah/shenandoahHeap.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
void ShenandoahController::pacing_notify_alloc(size_t words) {
assert(ShenandoahPacing, "should only call when pacing is enabled");
Atomic::add(&_allocs_seen, words, memory_order_relaxed);
}
size_t ShenandoahController::reset_allocs_seen() {
return Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed);
}
void ShenandoahController::prepare_for_graceful_shutdown() {
_graceful_shutdown.set();
}
bool ShenandoahController::in_graceful_shutdown() {
return _graceful_shutdown.is_set();
}
void ShenandoahController::update_gc_id() {
Atomic::inc(&_gc_id);
}
size_t ShenandoahController::get_gc_id() {
return Atomic::load(&_gc_id);
}
void ShenandoahController::handle_alloc_failure(ShenandoahAllocRequest& req, bool block) {
ShenandoahHeap* heap = ShenandoahHeap::heap();
assert(current()->is_Java_thread(), "expect Java thread here");
bool is_humongous = req.size() > ShenandoahHeapRegion::humongous_threshold_words();
if (try_set_alloc_failure_gc(is_humongous)) {
// Only report the first allocation failure
log_info(gc)("Failed to allocate %s, " SIZE_FORMAT "%s",
req.type_string(),
byte_size_in_proper_unit(req.size() * HeapWordSize), proper_unit_for_byte_size(req.size() * HeapWordSize));
// Now that alloc failure GC is scheduled, we can abort everything else
heap->cancel_gc(GCCause::_allocation_failure);
}
if (block) {
MonitorLocker ml(&_alloc_failure_waiters_lock);
while (is_alloc_failure_gc()) {
ml.wait();
}
}
}
void ShenandoahController::handle_alloc_failure_evac(size_t words) {
ShenandoahHeap* heap = ShenandoahHeap::heap();
bool is_humongous = (words > ShenandoahHeapRegion::region_size_words());
if (try_set_alloc_failure_gc(is_humongous)) {
// Only report the first allocation failure
log_info(gc)("Failed to allocate " SIZE_FORMAT "%s for evacuation",
byte_size_in_proper_unit(words * HeapWordSize), proper_unit_for_byte_size(words * HeapWordSize));
}
// Forcefully report allocation failure
heap->cancel_gc(GCCause::_shenandoah_allocation_failure_evac);
}
void ShenandoahController::notify_alloc_failure_waiters() {
_alloc_failure_gc.unset();
_humongous_alloc_failure_gc.unset();
MonitorLocker ml(&_alloc_failure_waiters_lock);
ml.notify_all();
}
bool ShenandoahController::try_set_alloc_failure_gc(bool is_humongous) {
if (is_humongous) {
_humongous_alloc_failure_gc.try_set();
}
return _alloc_failure_gc.try_set();
}
bool ShenandoahController::is_alloc_failure_gc() {
return _alloc_failure_gc.is_set();
}

@ -0,0 +1,105 @@
/*
* Copyright Amazon.com Inc. 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 LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP
#define LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP
#include "gc/shared/gcCause.hpp"
#include "gc/shared/concurrentGCThread.hpp"
#include "gc/shenandoah/shenandoahAllocRequest.hpp"
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
/**
* This interface exposes methods necessary for the heap to interact
* with the threads responsible for driving the collection cycle.
*/
class ShenandoahController: public ConcurrentGCThread {
private:
ShenandoahSharedFlag _graceful_shutdown;
shenandoah_padding(0);
volatile size_t _allocs_seen;
shenandoah_padding(1);
volatile size_t _gc_id;
shenandoah_padding(2);
protected:
ShenandoahSharedFlag _alloc_failure_gc;
ShenandoahSharedFlag _humongous_alloc_failure_gc;
// While we could have a single lock for these, it may risk unblocking
// GC waiters when alloc failure GC cycle finishes. We want instead
// to make complete explicit cycle for demanding customers.
Monitor _alloc_failure_waiters_lock;
Monitor _gc_waiters_lock;
public:
ShenandoahController():
ConcurrentGCThread(),
_allocs_seen(0),
_gc_id(0),
_alloc_failure_waiters_lock(Mutex::safepoint-2, "ShenandoahAllocFailureGC_lock", true),
_gc_waiters_lock(Mutex::safepoint-2, "ShenandoahRequestedGC_lock", true)
{ }
// Request a collection cycle. This handles "explicit" gc requests
// like System.gc and "implicit" gc requests, like metaspace oom.
virtual void request_gc(GCCause::Cause cause) = 0;
// This cancels the collection cycle and has an option to block
// until another cycle runs and clears the alloc failure gc flag.
void handle_alloc_failure(ShenandoahAllocRequest& req, bool block);
// Invoked for allocation failures during evacuation. This cancels
// the collection cycle without blocking.
void handle_alloc_failure_evac(size_t words);
// Return true if setting the flag which indicates allocation failure succeeds.
bool try_set_alloc_failure_gc(bool is_humongous);
// Notify threads waiting for GC to complete.
void notify_alloc_failure_waiters();
// True if allocation failure flag has been set.
bool is_alloc_failure_gc();
// This is called for every allocation. The control thread accumulates
// this value when idle. During the gc cycle, the control resets it
// and reports it to the pacer.
void pacing_notify_alloc(size_t words);
size_t reset_allocs_seen();
// These essentially allows to cancel a collection cycle for the
// purpose of shutting down the JVM, without trying to start a degenerated
// cycle.
void prepare_for_graceful_shutdown();
bool in_graceful_shutdown();
// Returns the internal gc count used by the control thread. Probably
// doesn't need to be exposed.
size_t get_gc_id();
void update_gc_id();
};
#endif //LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP

@ -955,7 +955,7 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) {
size_t original_count = shenandoah_policy()->full_gc_count();
while (result == nullptr
&& (get_gc_no_progress_count() == 0 || original_count == shenandoah_policy()->full_gc_count())) {
control_thread()->handle_alloc_failure(req);
control_thread()->handle_alloc_failure(req, true);
result = allocate_memory_under_lock(req, in_new_region);
}