8261759: ZGC: ZWorker Threads Continue Marking After System.exit() called

Reviewed-by: sjohanss, ayang
This commit is contained in:
Per Liden 2021-04-26 12:15:03 +00:00
parent 31abe68fa4
commit c3ac6900e7
10 changed files with 191 additions and 17 deletions

@ -0,0 +1,32 @@
/*
* Copyright (c) 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
* 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/z/zAbort.hpp"
#include "runtime/atomic.hpp"
volatile bool ZAbort::_should_abort = false;
void ZAbort::abort() {
Atomic::release_store_fence(&_should_abort, true);
}

@ -0,0 +1,38 @@
/*
* Copyright (c) 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
* 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 SHARE_GC_Z_ZABORT_HPP
#define SHARE_GC_Z_ZABORT_HPP
#include "memory/allocation.hpp"
class ZAbort : public AllStatic {
private:
static volatile bool _should_abort;
public:
static bool should_abort();
static void abort();
};
#endif // SHARE_GC_Z_ZABORT_HPP

@ -0,0 +1,34 @@
/*
* Copyright (c) 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
* 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 SHARE_GC_Z_ZABORT_INLINE_HPP
#define SHARE_GC_Z_ZABORT_INLINE_HPP
#include "gc/z/zAbort.hpp"
#include "runtime/atomic.hpp"
inline bool ZAbort::should_abort() {
return Atomic::load_acquire(&_should_abort);
}
#endif // SHARE_GC_Z_ZABORT_INLINE_HPP

@ -26,6 +26,7 @@
#include "gc/shared/gcLocker.hpp"
#include "gc/shared/gcVMOperations.hpp"
#include "gc/shared/isGCActiveMark.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zBreakpoint.hpp"
#include "gc/z/zCollectedHeap.hpp"
#include "gc/z/zDriver.hpp"
@ -397,6 +398,19 @@ public:
}
};
// Macro to execute a termination check after a concurrent phase. Note
// that it's important that the termination check comes after the call
// to the function f, since we can't abort between pause_relocate_start()
// and concurrent_relocate(). We need to let concurrent_relocate() call
// abort_page() on the remaining entries in the relocation set.
#define concurrent(f) \
do { \
concurrent_##f(); \
if (should_terminate()) { \
return; \
} \
} while (false)
void ZDriver::gc(GCCause::Cause cause) {
ZDriverGCScope scope(cause);
@ -404,31 +418,31 @@ void ZDriver::gc(GCCause::Cause cause) {
pause_mark_start();
// Phase 2: Concurrent Mark
concurrent_mark();
concurrent(mark);
// Phase 3: Pause Mark End
while (!pause_mark_end()) {
// Phase 3.5: Concurrent Mark Continue
concurrent_mark_continue();
concurrent(mark_continue);
}
// Phase 4: Concurrent Process Non-Strong References
concurrent_process_non_strong_references();
concurrent(process_non_strong_references);
// Phase 5: Concurrent Reset Relocation Set
concurrent_reset_relocation_set();
concurrent(reset_relocation_set);
// Phase 6: Pause Verify
pause_verify();
// Phase 7: Concurrent Select Relocation Set
concurrent_select_relocation_set();
concurrent(select_relocation_set);
// Phase 8: Pause Relocate Start
pause_relocate_start();
// Phase 9: Concurrent Relocate
concurrent_relocate();
concurrent(relocate);
}
void ZDriver::run_service() {
@ -456,5 +470,6 @@ void ZDriver::run_service() {
}
void ZDriver::stop_service() {
ZAbort::abort();
_gc_cycle_port.send_async(GCCause::_no_gc);
}

@ -58,7 +58,8 @@ bool ZForwarding::retain_page() {
if (ref_count < 0) {
// Claimed
wait_page_released();
const bool success = wait_page_released();
assert(success, "Should always succeed");
return false;
}
@ -129,14 +130,24 @@ void ZForwarding::release_page() {
}
}
void ZForwarding::wait_page_released() const {
bool ZForwarding::wait_page_released() const {
if (Atomic::load_acquire(&_ref_count) != 0) {
ZStatTimer timer(ZCriticalPhaseRelocationStall);
ZLocker<ZConditionLock> locker(&_ref_lock);
if (_ref_abort) {
return false;
}
ZStatTimer timer(ZCriticalPhaseRelocationStall);
while (Atomic::load_acquire(&_ref_count) != 0) {
if (_ref_abort) {
return false;
}
_ref_lock.wait();
}
}
return true;
}
ZPage* ZForwarding::detach_page() {
@ -154,6 +165,14 @@ ZPage* ZForwarding::detach_page() {
return page;
}
void ZForwarding::abort_page() {
ZLocker<ZConditionLock> locker(&_ref_lock);
assert(Atomic::load(&_ref_count) > 0, "Invalid state");
assert(!_ref_abort, "Invalid state");
_ref_abort = true;
_ref_lock.notify_all();
}
void ZForwarding::verify() const {
guarantee(_ref_count != 0, "Invalid reference count");
guarantee(_page != NULL, "Invalid page");

@ -48,6 +48,7 @@ private:
ZPage* _page;
mutable ZConditionLock _ref_lock;
volatile int32_t _ref_count;
bool _ref_abort;
bool _in_place;
ZForwardingEntry* entries() const;
@ -70,8 +71,9 @@ public:
bool retain_page();
ZPage* claim_page();
void release_page();
void wait_page_released() const;
bool wait_page_released() const;
ZPage* detach_page();
void abort_page();
void set_in_place();
bool in_place() const;

@ -59,6 +59,7 @@ inline ZForwarding::ZForwarding(ZPage* page, size_t nentries) :
_page(page),
_ref_lock(),
_ref_count(1),
_ref_abort(false),
_in_place(false) {}
inline uint8_t ZForwarding::type() const {

@ -26,6 +26,7 @@
#include "classfile/classLoaderDataGraph.hpp"
#include "code/nmethod.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zBarrier.inline.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zLock.inline.hpp"
@ -346,7 +347,7 @@ bool ZMark::drain(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks, ZMarkCach
}
// Success
return true;
return !timeout->has_expired();
}
bool ZMark::try_steal_local(ZMarkStripe* stripe, ZMarkThreadLocalStacks* stacks) {
@ -497,7 +498,8 @@ bool ZMark::try_terminate() {
class ZMarkNoTimeout : public StackObj {
public:
bool has_expired() {
return false;
// No timeout, but check for signal to abort
return ZAbort::should_abort();
}
};
@ -506,7 +508,10 @@ void ZMark::work_without_timeout(ZMarkCache* cache, ZMarkStripe* stripe, ZMarkTh
ZMarkNoTimeout no_timeout;
for (;;) {
drain(stripe, stacks, cache, &no_timeout);
if (!drain(stripe, stacks, cache, &no_timeout)) {
// Abort
break;
}
if (try_steal(stripe, stacks)) {
// Stole work

@ -23,6 +23,7 @@
#include "precompiled.hpp"
#include "gc/shared/gc_globals.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zAddress.inline.hpp"
#include "gc/z/zBarrier.inline.hpp"
#include "gc/z/zForwarding.inline.hpp"
@ -103,9 +104,14 @@ uintptr_t ZRelocate::relocate_object(ZForwarding* forwarding, uintptr_t from_add
return to_addr;
}
// Failed to relocate object. Wait for a worker thread to
// complete relocation of this page, and then forward object.
forwarding->wait_page_released();
// Failed to relocate object. Wait for a worker thread to complete
// relocation of this page, and then forward the object. If the GC
// aborts the relocation phase before the page has been relocated,
// then wait return false and we just forward the object in-place.
if (!forwarding->wait_page_released()) {
// Forward object in-place
return forwarding_insert(forwarding, from_addr, from_addr, &cursor);
}
}
// Forward object
@ -339,8 +345,15 @@ public:
}
void do_forwarding(ZForwarding* forwarding) {
// Relocate objects
_forwarding = forwarding;
// Check if we should abort
if (ZAbort::should_abort()) {
_forwarding->abort_page();
return;
}
// Relocate objects
_forwarding->object_iterate(this);
// Verify

@ -23,6 +23,7 @@
#include "precompiled.hpp"
#include "gc/shared/gc_globals.hpp"
#include "gc/z/zAbort.inline.hpp"
#include "gc/z/zCollectedHeap.hpp"
#include "gc/z/zCPU.inline.hpp"
#include "gc/z/zGlobals.hpp"
@ -642,6 +643,12 @@ void ZStatPhaseCycle::register_start(const Ticks& start) const {
}
void ZStatPhaseCycle::register_end(const Ticks& start, const Ticks& end) const {
if (ZAbort::should_abort()) {
log_info(gc)("Garbage Collection (%s) Aborted",
GCCause::to_string(ZCollectedHeap::heap()->gc_cause()));
return;
}
timer()->register_gc_end(end);
ZCollectedHeap::heap()->print_heap_after_gc();
@ -712,6 +719,10 @@ void ZStatPhaseConcurrent::register_start(const Ticks& start) const {
}
void ZStatPhaseConcurrent::register_end(const Ticks& start, const Ticks& end) const {
if (ZAbort::should_abort()) {
return;
}
timer()->register_gc_concurrent_end(end);
const Tickspan duration = end - start;
@ -730,6 +741,10 @@ void ZStatSubPhase::register_start(const Ticks& start) const {
}
void ZStatSubPhase::register_end(const Ticks& start, const Ticks& end) const {
if (ZAbort::should_abort()) {
return;
}
ZTracer::tracer()->report_thread_phase(name(), start, end);
const Tickspan duration = end - start;