diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index bdf1e5b912857..4f71135084465 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -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(); -} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp index 9da25b1a73ced..be99ef1d865fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp @@ -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 @@ class ShenandoahControlThread: public ConcurrentGCThread { 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 diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp new file mode 100644 index 0000000000000..6d6d21c406623 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp @@ -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(); +} + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp new file mode 100644 index 0000000000000..2808f30b44459 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -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 diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index f270e16f53eb9..ee3f4e7d8eb8c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -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); }