8240239: Replace ConcurrentGCPhaseManager
Replace ConcurrentGCPhaseManager with ConcurrentGCBreakpoints Co-authored-by: Per Liden <per.liden@oracle.com> Reviewed-by: kbarrett, pliden, sangheki
This commit is contained in:
parent
9e2ab1e363
commit
9f334a1640
@ -66,6 +66,7 @@
|
||||
#include "gc/g1/heapRegion.inline.hpp"
|
||||
#include "gc/g1/heapRegionRemSet.hpp"
|
||||
#include "gc/g1/heapRegionSet.inline.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/shared/gcBehaviours.hpp"
|
||||
#include "gc/shared/gcHeapSummary.hpp"
|
||||
#include "gc/shared/gcId.hpp"
|
||||
@ -2003,6 +2004,7 @@ bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
|
||||
switch (cause) {
|
||||
case GCCause::_g1_humongous_allocation: return true;
|
||||
case GCCause::_g1_periodic_collection: return G1PeriodicGCInvokesConcurrent;
|
||||
case GCCause::_wb_breakpoint: return true;
|
||||
default: return is_user_requested_concurrent_full_gc(cause);
|
||||
}
|
||||
}
|
||||
@ -2173,24 +2175,42 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
old_marking_completed_after = _old_marking_cycles_completed;
|
||||
}
|
||||
|
||||
if (!GCCause::is_user_requested_gc(cause)) {
|
||||
if (cause == GCCause::_wb_breakpoint) {
|
||||
if (op.gc_succeeded()) {
|
||||
LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true);
|
||||
return true;
|
||||
}
|
||||
// When _wb_breakpoint there can't be another cycle or deferred.
|
||||
assert(!op.cycle_already_in_progress(), "invariant");
|
||||
assert(!op.whitebox_attached(), "invariant");
|
||||
// Concurrent cycle attempt might have been cancelled by some other
|
||||
// collection, so retry. Unlike other cases below, we want to retry
|
||||
// even if cancelled by a STW full collection, because we really want
|
||||
// to start a concurrent cycle.
|
||||
if (old_marking_started_before != old_marking_started_after) {
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "ignoring STW full GC");
|
||||
old_marking_started_before = old_marking_started_after;
|
||||
}
|
||||
} else if (!GCCause::is_user_requested_gc(cause)) {
|
||||
// For an "automatic" (not user-requested) collection, we just need to
|
||||
// ensure that progress is made.
|
||||
//
|
||||
// Request is finished if any of
|
||||
// (1) the VMOp successfully performed a GC,
|
||||
// (2) a concurrent cycle was already in progress,
|
||||
// (3) a new cycle was started (by this thread or some other), or
|
||||
// (4) a Full GC was performed.
|
||||
// Cases (3) and (4) are detected together by a change to
|
||||
// (3) whitebox is controlling concurrent cycles,
|
||||
// (4) a new cycle was started (by this thread or some other), or
|
||||
// (5) a Full GC was performed.
|
||||
// Cases (4) and (5) are detected together by a change to
|
||||
// _old_marking_cycles_started.
|
||||
//
|
||||
// Note that (1) does not imply (3). If we're still in the mixed
|
||||
// Note that (1) does not imply (4). If we're still in the mixed
|
||||
// phase of an earlier concurrent collection, the request to make the
|
||||
// collection an initial-mark won't be honored. If we don't check for
|
||||
// both conditions we'll spin doing back-to-back collections.
|
||||
if (op.gc_succeeded() ||
|
||||
op.cycle_already_in_progress() ||
|
||||
op.whitebox_attached() ||
|
||||
(old_marking_started_before != old_marking_started_after)) {
|
||||
LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true);
|
||||
return true;
|
||||
@ -2244,20 +2264,32 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
// a new cycle was started.
|
||||
assert(!op.gc_succeeded(), "invariant");
|
||||
|
||||
// If VMOp failed because a cycle was already in progress, it is now
|
||||
// complete. But it didn't finish this user-requested GC, so try
|
||||
// again.
|
||||
if (op.cycle_already_in_progress()) {
|
||||
// If VMOp failed because a cycle was already in progress, it
|
||||
// is now complete. But it didn't finish this user-requested
|
||||
// GC, so try again.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress");
|
||||
continue;
|
||||
} else if (op.whitebox_attached()) {
|
||||
// If WhiteBox wants control, wait for notification of a state
|
||||
// change in the controller, then try again. Don't wait for
|
||||
// release of control, since collections may complete while in
|
||||
// control. Note: This won't recognize a STW full collection
|
||||
// while waiting; we can't wait on multiple monitors.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "whitebox control stall");
|
||||
MonitorLocker ml(ConcurrentGCBreakpoints::monitor());
|
||||
if (ConcurrentGCBreakpoints::is_controlled()) {
|
||||
ml.wait();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Collection failed and should be retried.
|
||||
assert(op.transient_failure(), "invariant");
|
||||
|
||||
// If GCLocker is active, wait until clear before retrying.
|
||||
if (GCLocker::is_active_and_needs_gc()) {
|
||||
// If GCLocker is active, wait until clear before retrying.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "gc-locker stall");
|
||||
GCLocker::stall_until_clear();
|
||||
}
|
||||
@ -2453,14 +2485,10 @@ void G1CollectedHeap::verify(VerifyOption vo) {
|
||||
_verifier->verify(vo);
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::supports_concurrent_phase_control() const {
|
||||
bool G1CollectedHeap::supports_concurrent_gc_breakpoints() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::request_concurrent_phase(const char* phase) {
|
||||
return _cm_thread->request_concurrent_phase(phase);
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::is_heterogeneous_heap() const {
|
||||
return G1Arguments::is_heterogeneous_heap();
|
||||
}
|
||||
@ -3178,6 +3206,7 @@ void G1CollectedHeap::do_collection_pause_at_safepoint_helper(double target_paus
|
||||
// Note: of course, the actual marking work will not start until the safepoint
|
||||
// itself is released in SuspendibleThreadSet::desynchronize().
|
||||
do_concurrent_mark();
|
||||
ConcurrentGCBreakpoints::notify_idle_to_active();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ private:
|
||||
// (a) cause == _g1_humongous_allocation,
|
||||
// (b) cause == _java_lang_system_gc and +ExplicitGCInvokesConcurrent,
|
||||
// (c) cause == _dcmd_gc_run and +ExplicitGCInvokesConcurrent,
|
||||
// (d) cause == _wb_conc_mark,
|
||||
// (d) cause == _wb_conc_mark or _wb_breakpoint,
|
||||
// (e) cause == _g1_periodic_collection and +G1PeriodicGCInvokesConcurrent.
|
||||
bool should_do_concurrent_full_gc(GCCause::Cause cause);
|
||||
|
||||
@ -1423,8 +1423,7 @@ public:
|
||||
void verify(VerifyOption vo);
|
||||
|
||||
// WhiteBox testing support.
|
||||
virtual bool supports_concurrent_phase_control() const;
|
||||
virtual bool request_concurrent_phase(const char* phase);
|
||||
virtual bool supports_concurrent_gc_breakpoints() const;
|
||||
bool is_heterogeneous_heap() const;
|
||||
|
||||
virtual WorkGang* get_safepoint_workers() { return _workers; }
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
@ -33,7 +33,7 @@
|
||||
#include "gc/g1/g1RemSet.hpp"
|
||||
#include "gc/g1/g1Trace.hpp"
|
||||
#include "gc/g1/g1VMOperations.hpp"
|
||||
#include "gc/shared/concurrentGCPhaseManager.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/shared/gcId.hpp"
|
||||
#include "gc/shared/gcTraceTime.inline.hpp"
|
||||
#include "gc/shared/suspendibleThreadSet.hpp"
|
||||
@ -45,44 +45,14 @@
|
||||
|
||||
// ======= Concurrent Mark Thread ========
|
||||
|
||||
// Check order in EXPAND_CURRENT_PHASES
|
||||
STATIC_ASSERT(ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE <
|
||||
ConcurrentGCPhaseManager::IDLE_PHASE);
|
||||
|
||||
#define EXPAND_CONCURRENT_PHASES(expander) \
|
||||
expander(ANY, = ConcurrentGCPhaseManager::UNCONSTRAINED_PHASE, NULL) \
|
||||
expander(IDLE, = ConcurrentGCPhaseManager::IDLE_PHASE, NULL) \
|
||||
expander(CONCURRENT_CYCLE,, "Concurrent Cycle") \
|
||||
expander(CLEAR_CLAIMED_MARKS,, "Concurrent Clear Claimed Marks") \
|
||||
expander(SCAN_ROOT_REGIONS,, "Concurrent Scan Root Regions") \
|
||||
expander(CONCURRENT_MARK,, "Concurrent Mark") \
|
||||
expander(MARK_FROM_ROOTS,, "Concurrent Mark From Roots") \
|
||||
expander(PRECLEAN,, "Concurrent Preclean") \
|
||||
expander(BEFORE_REMARK,, NULL) \
|
||||
expander(REMARK,, NULL) \
|
||||
expander(REBUILD_REMEMBERED_SETS,, "Concurrent Rebuild Remembered Sets") \
|
||||
expander(CLEANUP_FOR_NEXT_MARK,, "Concurrent Cleanup for Next Mark") \
|
||||
/* */
|
||||
|
||||
class G1ConcurrentPhase : public AllStatic {
|
||||
public:
|
||||
enum {
|
||||
#define CONCURRENT_PHASE_ENUM(tag, value, ignore_title) tag value,
|
||||
EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_ENUM)
|
||||
#undef CONCURRENT_PHASE_ENUM
|
||||
PHASE_ID_LIMIT
|
||||
};
|
||||
};
|
||||
|
||||
G1ConcurrentMarkThread::G1ConcurrentMarkThread(G1ConcurrentMark* cm) :
|
||||
ConcurrentGCThread(),
|
||||
_vtime_start(0.0),
|
||||
_vtime_accum(0.0),
|
||||
_vtime_mark_accum(0.0),
|
||||
_cm(cm),
|
||||
_state(Idle),
|
||||
_phase_manager_stack() {
|
||||
|
||||
_state(Idle)
|
||||
{
|
||||
set_name("G1 Main Marker");
|
||||
create_and_start();
|
||||
}
|
||||
@ -163,102 +133,12 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImpl<LogLevel::Info, LOG_TAGS(gc,
|
||||
}
|
||||
};
|
||||
|
||||
static const char* const concurrent_phase_names[] = {
|
||||
#define CONCURRENT_PHASE_NAME(tag, ignore_value, ignore_title) XSTR(tag),
|
||||
EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_NAME)
|
||||
#undef CONCURRENT_PHASE_NAME
|
||||
NULL // terminator
|
||||
};
|
||||
// Verify dense enum assumption. +1 for terminator.
|
||||
STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT + 1 ==
|
||||
ARRAY_SIZE(concurrent_phase_names));
|
||||
|
||||
// Returns the phase number for name, or a negative value if unknown.
|
||||
static int lookup_concurrent_phase(const char* name) {
|
||||
const char* const* names = concurrent_phase_names;
|
||||
for (uint i = 0; names[i] != NULL; ++i) {
|
||||
if (strcmp(name, names[i]) == 0) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The phase must be valid and must have a title.
|
||||
static const char* lookup_concurrent_phase_title(int phase) {
|
||||
static const char* const titles[] = {
|
||||
#define CONCURRENT_PHASE_TITLE(ignore_tag, ignore_value, title) title,
|
||||
EXPAND_CONCURRENT_PHASES(CONCURRENT_PHASE_TITLE)
|
||||
#undef CONCURRENT_PHASE_TITLE
|
||||
};
|
||||
// Verify dense enum assumption.
|
||||
STATIC_ASSERT(G1ConcurrentPhase::PHASE_ID_LIMIT == ARRAY_SIZE(titles));
|
||||
|
||||
assert(0 <= phase, "precondition");
|
||||
assert((uint)phase < ARRAY_SIZE(titles), "precondition");
|
||||
const char* title = titles[phase];
|
||||
assert(title != NULL, "precondition");
|
||||
return title;
|
||||
}
|
||||
|
||||
class G1ConcPhaseManager : public StackObj {
|
||||
G1ConcurrentMark* _cm;
|
||||
ConcurrentGCPhaseManager _manager;
|
||||
|
||||
public:
|
||||
G1ConcPhaseManager(int phase, G1ConcurrentMarkThread* thread) :
|
||||
_cm(thread->cm()),
|
||||
_manager(phase, thread->phase_manager_stack())
|
||||
{ }
|
||||
|
||||
~G1ConcPhaseManager() {
|
||||
// Deactivate the manager if marking aborted, to avoid blocking on
|
||||
// phase exit when the phase has been requested.
|
||||
if (_cm->has_aborted()) {
|
||||
_manager.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void set_phase(int phase, bool force) {
|
||||
_manager.set_phase(phase, force);
|
||||
}
|
||||
};
|
||||
|
||||
// Combine phase management and timing into one convenient utility.
|
||||
class G1ConcPhase : public StackObj {
|
||||
G1ConcPhaseTimer _timer;
|
||||
G1ConcPhaseManager _manager;
|
||||
|
||||
public:
|
||||
G1ConcPhase(int phase, G1ConcurrentMarkThread* thread) :
|
||||
_timer(thread->cm(), lookup_concurrent_phase_title(phase)),
|
||||
_manager(phase, thread)
|
||||
{ }
|
||||
};
|
||||
|
||||
bool G1ConcurrentMarkThread::request_concurrent_phase(const char* phase_name) {
|
||||
int phase = lookup_concurrent_phase(phase_name);
|
||||
if (phase < 0) return false;
|
||||
|
||||
while (!ConcurrentGCPhaseManager::wait_for_phase(phase,
|
||||
phase_manager_stack())) {
|
||||
assert(phase != G1ConcurrentPhase::ANY, "Wait for ANY phase must succeed");
|
||||
if ((phase != G1ConcurrentPhase::IDLE) && !during_cycle()) {
|
||||
// If idle and the goal is !idle, start a collection.
|
||||
G1CollectedHeap::heap()->collect(GCCause::_wb_conc_mark);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void G1ConcurrentMarkThread::run_service() {
|
||||
_vtime_start = os::elapsedVTime();
|
||||
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
G1Policy* policy = g1h->policy();
|
||||
|
||||
G1ConcPhaseManager cpmanager(G1ConcurrentPhase::IDLE, this);
|
||||
|
||||
while (!should_terminate()) {
|
||||
// wait until started is set.
|
||||
sleep_before_next_cycle();
|
||||
@ -266,8 +146,6 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
break;
|
||||
}
|
||||
|
||||
cpmanager.set_phase(G1ConcurrentPhase::CONCURRENT_CYCLE, false /* force */);
|
||||
|
||||
GCIdMark gc_id_mark;
|
||||
|
||||
_cm->concurrent_cycle_start();
|
||||
@ -279,7 +157,7 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
double cycle_start = os::elapsedVTime();
|
||||
|
||||
{
|
||||
G1ConcPhase p(G1ConcurrentPhase::CLEAR_CLAIMED_MARKS, this);
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Clear Claimed Marks");
|
||||
ClassLoaderDataGraph::clear_claimed_marks();
|
||||
}
|
||||
|
||||
@ -292,78 +170,70 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
// correctness issue.
|
||||
|
||||
{
|
||||
G1ConcPhase p(G1ConcurrentPhase::SCAN_ROOT_REGIONS, this);
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions");
|
||||
_cm->scan_root_regions();
|
||||
}
|
||||
|
||||
// It would be nice to use the G1ConcPhase class here but
|
||||
// Note: ConcurrentGCBreakpoints before here risk deadlock,
|
||||
// because a young GC must wait for root region scanning.
|
||||
|
||||
// It would be nice to use the G1ConcPhaseTimer class here but
|
||||
// the "end" logging is inside the loop and not at the end of
|
||||
// a scope. Also, the timer doesn't support nesting.
|
||||
// Mimicking the same log output instead.
|
||||
{
|
||||
G1ConcPhaseManager mark_manager(G1ConcurrentPhase::CONCURRENT_MARK, this);
|
||||
jlong mark_start = os::elapsed_counter();
|
||||
const char* cm_title = lookup_concurrent_phase_title(G1ConcurrentPhase::CONCURRENT_MARK);
|
||||
log_info(gc, marking)("%s (%.3fs)",
|
||||
cm_title,
|
||||
TimeHelper::counter_to_seconds(mark_start));
|
||||
for (uint iter = 1; !_cm->has_aborted(); ++iter) {
|
||||
// Concurrent marking.
|
||||
{
|
||||
G1ConcPhase p(G1ConcurrentPhase::MARK_FROM_ROOTS, this);
|
||||
_cm->mark_from_roots();
|
||||
}
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
jlong mark_start = os::elapsed_counter();
|
||||
log_info(gc, marking)("Concurrent Mark (%.3fs)",
|
||||
TimeHelper::counter_to_seconds(mark_start));
|
||||
for (uint iter = 1; !_cm->has_aborted(); ++iter) {
|
||||
// Concurrent marking.
|
||||
{
|
||||
ConcurrentGCBreakpoints::at("AFTER MARKING STARTED");
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Mark From Roots");
|
||||
_cm->mark_from_roots();
|
||||
}
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (G1UseReferencePrecleaning) {
|
||||
G1ConcPhase p(G1ConcurrentPhase::PRECLEAN, this);
|
||||
_cm->preclean();
|
||||
}
|
||||
if (G1UseReferencePrecleaning) {
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Preclean");
|
||||
_cm->preclean();
|
||||
}
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Provide a control point before remark.
|
||||
{
|
||||
G1ConcPhaseManager p(G1ConcurrentPhase::BEFORE_REMARK, this);
|
||||
}
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
// Delay remark pause for MMU.
|
||||
double mark_end_time = os::elapsedVTime();
|
||||
jlong mark_end = os::elapsed_counter();
|
||||
_vtime_mark_accum += (mark_end_time - cycle_start);
|
||||
delay_to_keep_mmu(policy, true /* remark */);
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Delay remark pause for MMU.
|
||||
double mark_end_time = os::elapsedVTime();
|
||||
jlong mark_end = os::elapsed_counter();
|
||||
_vtime_mark_accum += (mark_end_time - cycle_start);
|
||||
delay_to_keep_mmu(policy, true /* remark */);
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Pause Remark.
|
||||
log_info(gc, marking)("%s (%.3fs, %.3fs) %.3fms",
|
||||
cm_title,
|
||||
TimeHelper::counter_to_seconds(mark_start),
|
||||
TimeHelper::counter_to_seconds(mark_end),
|
||||
TimeHelper::counter_to_millis(mark_end - mark_start));
|
||||
mark_manager.set_phase(G1ConcurrentPhase::REMARK, false);
|
||||
CMRemark cl(_cm);
|
||||
VM_G1Concurrent op(&cl, "Pause Remark");
|
||||
VMThread::execute(&op);
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
} else if (!_cm->restart_for_overflow()) {
|
||||
break; // Exit loop if no restart requested.
|
||||
} else {
|
||||
// Loop to restart for overflow.
|
||||
mark_manager.set_phase(G1ConcurrentPhase::CONCURRENT_MARK, false);
|
||||
log_info(gc, marking)("%s Restart for Mark Stack Overflow (iteration #%u)",
|
||||
cm_title, iter);
|
||||
}
|
||||
// Pause Remark.
|
||||
ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED");
|
||||
log_info(gc, marking)("Concurrent Mark (%.3fs, %.3fs) %.3fms",
|
||||
TimeHelper::counter_to_seconds(mark_start),
|
||||
TimeHelper::counter_to_seconds(mark_end),
|
||||
TimeHelper::counter_to_millis(mark_end - mark_start));
|
||||
CMRemark cl(_cm);
|
||||
VM_G1Concurrent op(&cl, "Pause Remark");
|
||||
VMThread::execute(&op);
|
||||
if (_cm->has_aborted()) {
|
||||
break;
|
||||
} else if (!_cm->restart_for_overflow()) {
|
||||
break; // Exit loop if no restart requested.
|
||||
} else {
|
||||
// Loop to restart for overflow.
|
||||
log_info(gc, marking)("Concurrent Mark Restart for Mark Stack Overflow (iteration #%u)",
|
||||
iter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cm->has_aborted()) {
|
||||
G1ConcPhase p(G1ConcurrentPhase::REBUILD_REMEMBERED_SETS, this);
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Rebuild Remembered Sets");
|
||||
_cm->rebuild_rem_set_concurrently();
|
||||
}
|
||||
|
||||
@ -387,7 +257,7 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
// We may have aborted just before the remark. Do not bother clearing the
|
||||
// bitmap then, as it has been done during mark abort.
|
||||
if (!_cm->has_aborted()) {
|
||||
G1ConcPhase p(G1ConcurrentPhase::CLEANUP_FOR_NEXT_MARK, this);
|
||||
G1ConcPhaseTimer p(_cm, "Concurrent Cleanup for Next Mark");
|
||||
_cm->cleanup_for_next_mark();
|
||||
}
|
||||
}
|
||||
@ -401,9 +271,8 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
g1h->increment_old_marking_cycles_completed(true /* concurrent */);
|
||||
|
||||
_cm->concurrent_cycle_end();
|
||||
ConcurrentGCBreakpoints::notify_active_to_idle();
|
||||
}
|
||||
|
||||
cpmanager.set_phase(G1ConcurrentPhase::IDLE, _cm->has_aborted() /* force */);
|
||||
}
|
||||
_cm->root_regions()->cancel_scan();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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,7 +25,6 @@
|
||||
#ifndef SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP
|
||||
#define SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP
|
||||
|
||||
#include "gc/shared/concurrentGCPhaseManager.hpp"
|
||||
#include "gc/shared/concurrentGCThread.hpp"
|
||||
|
||||
class G1ConcurrentMark;
|
||||
@ -50,9 +49,6 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread {
|
||||
|
||||
volatile State _state;
|
||||
|
||||
// WhiteBox testing support.
|
||||
ConcurrentGCPhaseManager::Stack _phase_manager_stack;
|
||||
|
||||
void sleep_before_next_cycle();
|
||||
// Delay marking to meet MMU.
|
||||
void delay_to_keep_mmu(G1Policy* g1_policy, bool remark);
|
||||
@ -88,13 +84,6 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread {
|
||||
// as the CM thread might take some time to wake up before noticing
|
||||
// that started() is set and set in_progress().
|
||||
bool during_cycle() { return !idle(); }
|
||||
|
||||
// WhiteBox testing support.
|
||||
bool request_concurrent_phase(const char* phase);
|
||||
|
||||
ConcurrentGCPhaseManager::Stack* phase_manager_stack() {
|
||||
return &_phase_manager_stack;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
@ -41,6 +41,7 @@
|
||||
#include "gc/g1/g1YoungGenSizer.hpp"
|
||||
#include "gc/g1/heapRegion.inline.hpp"
|
||||
#include "gc/g1/heapRegionRemSet.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/shared/gcPolicyCounters.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
@ -1057,15 +1058,23 @@ void G1Policy::decide_on_conc_mark_initiation() {
|
||||
if (collector_state()->initiate_conc_mark_if_possible()) {
|
||||
// We had noticed on a previous pause that the heap occupancy has
|
||||
// gone over the initiating threshold and we should start a
|
||||
// concurrent marking cycle. So we might initiate one.
|
||||
// concurrent marking cycle. Or we've been explicitly requested
|
||||
// to start a concurrent marking cycle. Either way, we initiate
|
||||
// one if not inhibited for some reason.
|
||||
|
||||
if (!about_to_start_mixed_phase() && collector_state()->in_young_only_phase()) {
|
||||
GCCause::Cause cause = _g1h->gc_cause();
|
||||
if ((cause != GCCause::_wb_breakpoint) &&
|
||||
ConcurrentGCBreakpoints::is_controlled()) {
|
||||
log_debug(gc, ergo)("Do not initiate concurrent cycle (whitebox controlled)");
|
||||
} else if (!about_to_start_mixed_phase() && collector_state()->in_young_only_phase()) {
|
||||
// Initiate a new initial mark if there is no marking or reclamation going on.
|
||||
initiate_conc_mark();
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (concurrent cycle initiation requested)");
|
||||
} else if (_g1h->is_user_requested_concurrent_full_gc(_g1h->gc_cause())) {
|
||||
// Initiate a user requested initial mark. An initial mark must be young only
|
||||
// GC, so the collector state must be updated to reflect this.
|
||||
} else if (_g1h->is_user_requested_concurrent_full_gc(cause) ||
|
||||
(cause == GCCause::_wb_breakpoint)) {
|
||||
// Initiate a user requested initial mark or run_to a breakpoint.
|
||||
// An initial mark must be young only GC, so the collector state
|
||||
// must be updated to reflect this.
|
||||
collector_state()->set_in_young_only_phase(true);
|
||||
collector_state()->set_in_young_gc_before_mixed(false);
|
||||
|
||||
@ -1076,7 +1085,8 @@ void G1Policy::decide_on_conc_mark_initiation() {
|
||||
clear_collection_set_candidates();
|
||||
abort_time_to_mixed_tracking();
|
||||
initiate_conc_mark();
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (user requested concurrent cycle)");
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (%s requested concurrent cycle)",
|
||||
(cause == GCCause::_wb_breakpoint) ? "run_to breakpoint" : "user");
|
||||
} else {
|
||||
// The concurrent marking thread is still finishing up the
|
||||
// previous cycle. If we start one right now the two cycles
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "gc/g1/g1ConcurrentMarkThread.inline.hpp"
|
||||
#include "gc/g1/g1Policy.hpp"
|
||||
#include "gc/g1/g1VMOperations.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/shared/gcCause.hpp"
|
||||
#include "gc/shared/gcId.hpp"
|
||||
#include "gc/shared/gcTimer.hpp"
|
||||
#include "gc/shared/gcTraceTime.inline.hpp"
|
||||
@ -47,6 +49,7 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before,
|
||||
_target_pause_time_ms(target_pause_time_ms),
|
||||
_transient_failure(false),
|
||||
_cycle_already_in_progress(false),
|
||||
_whitebox_attached(false),
|
||||
_terminating(false),
|
||||
_gc_succeeded(false)
|
||||
{}
|
||||
@ -82,6 +85,13 @@ void VM_G1TryInitiateConcMark::doit() {
|
||||
// there is already a concurrent marking cycle in progress. Set flag
|
||||
// to notify the caller and return immediately.
|
||||
_cycle_already_in_progress = true;
|
||||
} else if ((_gc_cause != GCCause::_wb_breakpoint) &&
|
||||
ConcurrentGCBreakpoints::is_controlled()) {
|
||||
// WhiteBox wants to be in control of concurrent cycles, so don't try to
|
||||
// start one. This check is after the force_initial_mark_xxx so that a
|
||||
// request will be remembered for a later partial collection, even though
|
||||
// we've rejected this request.
|
||||
_whitebox_attached = true;
|
||||
} else if (g1h->do_collection_pause_at_safepoint(_target_pause_time_ms)) {
|
||||
_gc_succeeded = true;
|
||||
} else {
|
||||
|
@ -48,6 +48,7 @@ class VM_G1TryInitiateConcMark : public VM_GC_Operation {
|
||||
double _target_pause_time_ms;
|
||||
bool _transient_failure;
|
||||
bool _cycle_already_in_progress;
|
||||
bool _whitebox_attached;
|
||||
bool _terminating;
|
||||
bool _gc_succeeded;
|
||||
|
||||
@ -60,6 +61,7 @@ public:
|
||||
virtual void doit();
|
||||
bool transient_failure() const { return _transient_failure; }
|
||||
bool cycle_already_in_progress() const { return _cycle_already_in_progress; }
|
||||
bool whitebox_attached() const { return _whitebox_attached; }
|
||||
bool terminating() const { return _terminating; }
|
||||
bool gc_succeeded() const { return _gc_succeeded; }
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
@ -162,14 +162,8 @@ void CollectedHeap::trace_heap_after_gc(const GCTracer* gc_tracer) {
|
||||
trace_heap(GCWhen::AfterGC, gc_tracer);
|
||||
}
|
||||
|
||||
// WhiteBox API support for concurrent collectors. These are the
|
||||
// default implementations, for collectors which don't support this
|
||||
// feature.
|
||||
bool CollectedHeap::supports_concurrent_phase_control() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollectedHeap::request_concurrent_phase(const char* phase) {
|
||||
// Default implementation, for collectors that don't support the feature.
|
||||
bool CollectedHeap::supports_concurrent_gc_breakpoints() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, 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
|
||||
@ -459,24 +459,9 @@ class CollectedHeap : public CHeapObj<mtInternal> {
|
||||
// Heap verification
|
||||
virtual void verify(VerifyOption option) = 0;
|
||||
|
||||
// Return true if concurrent phase control (via
|
||||
// request_concurrent_phase_control) is supported by this collector.
|
||||
// The default implementation returns false.
|
||||
virtual bool supports_concurrent_phase_control() const;
|
||||
|
||||
// Request the collector enter the indicated concurrent phase, and
|
||||
// wait until it does so. Supports WhiteBox testing. Only one
|
||||
// request may be active at a time. Phases are designated by name;
|
||||
// the set of names and their meaning is GC-specific. Once the
|
||||
// requested phase has been reached, the collector will attempt to
|
||||
// avoid transitioning to a new phase until a new request is made.
|
||||
// [Note: A collector might not be able to remain in a given phase.
|
||||
// For example, a full collection might cancel an in-progress
|
||||
// concurrent collection.]
|
||||
//
|
||||
// Returns true when the phase is reached. Returns false for an
|
||||
// unknown phase. The default implementation returns false.
|
||||
virtual bool request_concurrent_phase(const char* phase);
|
||||
// Return true if concurrent gc control via WhiteBox is supported by
|
||||
// this collector. The default implementation returns false.
|
||||
virtual bool supports_concurrent_gc_breakpoints() const;
|
||||
|
||||
// Provides a thread pool to SafepointSynchronize to use
|
||||
// for parallel safepoint cleanup.
|
||||
|
176
src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp
Normal file
176
src/hotspot/share/gc/shared/concurrentGCBreakpoints.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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/collectedHeap.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
// States:
|
||||
// _run_to _want_idle _is_stopped
|
||||
// (1) No active request NULL false false
|
||||
// (2) Active run_to() running non-NULL false false
|
||||
// (3) Active run_to() in at() NULL false true
|
||||
// (4) Active run_to_idle() NULL true false
|
||||
const char* ConcurrentGCBreakpoints::_run_to = NULL;
|
||||
bool ConcurrentGCBreakpoints::_want_idle = false;
|
||||
bool ConcurrentGCBreakpoints::_is_stopped = false;
|
||||
|
||||
// True if the collector is idle.
|
||||
bool ConcurrentGCBreakpoints::_is_idle = true;
|
||||
|
||||
void ConcurrentGCBreakpoints::reset_request_state() {
|
||||
_run_to = NULL;
|
||||
_want_idle = false;
|
||||
_is_stopped = false;
|
||||
}
|
||||
|
||||
Monitor* ConcurrentGCBreakpoints::monitor() {
|
||||
return ConcurrentGCBreakpoints_lock;
|
||||
}
|
||||
|
||||
bool ConcurrentGCBreakpoints::is_controlled() {
|
||||
assert_locked_or_safepoint(monitor());
|
||||
return _want_idle || _is_stopped || (_run_to != NULL);
|
||||
}
|
||||
|
||||
#define assert_Java_thread() \
|
||||
assert(Thread::current()->is_Java_thread(), "precondition")
|
||||
|
||||
void ConcurrentGCBreakpoints::run_to_idle_impl(bool acquiring_control) {
|
||||
assert_Java_thread();
|
||||
MonitorLocker ml(monitor());
|
||||
if (acquiring_control) {
|
||||
assert(!is_controlled(), "precondition");
|
||||
log_debug(gc, breakpoint)("acquire_control");
|
||||
} else {
|
||||
assert(is_controlled(), "precondition");
|
||||
log_debug(gc, breakpoint)("run_to_idle");
|
||||
}
|
||||
reset_request_state();
|
||||
_want_idle = true;
|
||||
ml.notify_all();
|
||||
while (!_is_idle) {
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::acquire_control() {
|
||||
run_to_idle_impl(true);
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::release_control() {
|
||||
assert_Java_thread();
|
||||
MonitorLocker ml(monitor());
|
||||
log_debug(gc, breakpoint)("release_control");
|
||||
reset_request_state();
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::run_to_idle() {
|
||||
run_to_idle_impl(false);
|
||||
}
|
||||
|
||||
bool ConcurrentGCBreakpoints::run_to(const char* breakpoint) {
|
||||
assert_Java_thread();
|
||||
assert(breakpoint != NULL, "precondition");
|
||||
|
||||
MonitorLocker ml(monitor());
|
||||
assert(is_controlled(), "precondition");
|
||||
log_debug(gc, breakpoint)("run_to %s", breakpoint);
|
||||
reset_request_state();
|
||||
_run_to = breakpoint;
|
||||
ml.notify_all();
|
||||
|
||||
if (_is_idle) {
|
||||
log_debug(gc, breakpoint)("run_to requesting collection %s", breakpoint);
|
||||
MutexUnlocker mul(monitor());
|
||||
Universe::heap()->collect(GCCause::_wb_breakpoint);
|
||||
}
|
||||
|
||||
// Wait for corresponding at() or a notify_idle().
|
||||
while (true) {
|
||||
if (_want_idle) {
|
||||
// Completed cycle and resumed idle without hitting requested stop.
|
||||
// That replaced our request with a run_to_idle() request.
|
||||
log_debug(gc, breakpoint)("run_to missed %s", breakpoint);
|
||||
return false; // Missed.
|
||||
} else if (_is_stopped) {
|
||||
log_debug(gc, breakpoint)("run_to stopped at %s", breakpoint);
|
||||
return true; // Success.
|
||||
} else {
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::at(const char* breakpoint) {
|
||||
assert(Thread::current()->is_ConcurrentGC_thread(), "precondition");
|
||||
assert(breakpoint != NULL, "precondition");
|
||||
MonitorLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
|
||||
|
||||
// Ignore non-matching request state.
|
||||
if ((_run_to == NULL) || (strcmp(_run_to, breakpoint) != 0)) {
|
||||
log_trace(gc, breakpoint)("unmatched breakpoint %s", breakpoint);
|
||||
return;
|
||||
}
|
||||
log_trace(gc, breakpoint)("matched breakpoint %s", breakpoint);
|
||||
|
||||
// Notify request.
|
||||
_run_to = NULL;
|
||||
_is_stopped = true;
|
||||
ml.notify_all(); // Wakeup waiting request.
|
||||
// Wait for request to be cancelled.
|
||||
while (_is_stopped) {
|
||||
ml.wait();
|
||||
}
|
||||
log_trace(gc, breakpoint)("resumed from breakpoint");
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::notify_active_to_idle() {
|
||||
MonitorLocker ml(monitor(), Mutex::_no_safepoint_check_flag);
|
||||
assert(!_is_stopped, "invariant");
|
||||
// Notify pending run_to request of miss by replacing the run_to() request
|
||||
// with a run_to_idle() request.
|
||||
if (_run_to != NULL) {
|
||||
log_debug(gc, breakpoint)("Now idle and clearing breakpoint %s", _run_to);
|
||||
_run_to = NULL;
|
||||
_want_idle = true;
|
||||
} else {
|
||||
log_debug(gc, breakpoint)("Now idle");
|
||||
}
|
||||
_is_idle = true;
|
||||
monitor()->notify_all();
|
||||
}
|
||||
|
||||
void ConcurrentGCBreakpoints::notify_idle_to_active() {
|
||||
assert_locked_or_safepoint(monitor());
|
||||
log_debug(gc, breakpoint)("Now active");
|
||||
_is_idle = false;
|
||||
}
|
136
src/hotspot/share/gc/shared/concurrentGCBreakpoints.hpp
Normal file
136
src/hotspot/share/gc/shared/concurrentGCBreakpoints.hpp
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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_SHARED_CONCURRENTGCBREAKPOINTS_HPP
|
||||
#define SHARE_GC_SHARED_CONCURRENTGCBREAKPOINTS_HPP
|
||||
|
||||
#include "gc/shared/gcCause.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
class Monitor;
|
||||
|
||||
class ConcurrentGCBreakpoints : public AllStatic {
|
||||
static const char* _run_to;
|
||||
static bool _want_idle;
|
||||
static bool _is_stopped;
|
||||
static bool _is_idle;
|
||||
|
||||
static void reset_request_state();
|
||||
static void run_to_idle_impl(bool acquiring_control);
|
||||
|
||||
public:
|
||||
// Monitor used by this facility.
|
||||
static Monitor* monitor();
|
||||
|
||||
// Returns true if this facility is controlling concurrent collections,
|
||||
// e.g. there has been an acquire_control() without a matching
|
||||
// release_control().
|
||||
//
|
||||
// precondition: Must be at a safepoint or have the monitor locked.
|
||||
// note: Does not lock the monitor.
|
||||
static bool is_controlled();
|
||||
|
||||
///////////
|
||||
// Functions for use by the application / mutator threads.
|
||||
// All of these functions lock the monitor.
|
||||
// All of these functions may safepoint.
|
||||
|
||||
// Take control of the concurrent collector. If a collection is in
|
||||
// progress, wait until it completes. On return the concurrent collector
|
||||
// will be idle and will remain so until a subsequent run_to() or
|
||||
// release_control().
|
||||
//
|
||||
// precondition: Calling thread must be a Java thread.
|
||||
// precondition: !is_controlled().
|
||||
// postcondition: is_controlled().
|
||||
static void acquire_control();
|
||||
|
||||
// Release control of the concurrent collector, cancelling any preceeding
|
||||
// run_to() or run_to_idle() request.
|
||||
//
|
||||
// precondition: Calling thread must be a Java thread.
|
||||
// precondition: Must not be a concurrent request operation.
|
||||
// postcondiiton: !is_controlled().
|
||||
static void release_control();
|
||||
|
||||
// Requests the concurrent collector to be idle. Cancels any preceeding
|
||||
// run_to() request. No new concurrent collections will be started while
|
||||
// the request is active. If a collection is already in progress, it is
|
||||
// allowed to complete before this function returns.
|
||||
//
|
||||
// precondition: Calling thread must be a Java thread.
|
||||
// precondition: Must not be a concurrent request or release operation.
|
||||
// precondition: is_controlled().
|
||||
// postcondition: is_controlled().
|
||||
static void run_to_idle();
|
||||
|
||||
// Requests the concurrent collector to run until the named breakpoint is
|
||||
// reached. Cancels any preceeding run_to_idle(). If the collector is
|
||||
// presently idle, starts a collection with cause GCCause::_wb_breakpoint.
|
||||
// If the collector is presently stopped at a breakpoint, the previous
|
||||
// request is replaced by the new request and the collector is allowed to
|
||||
// resume. Waits for a subsequent matching call to at(), or a call to
|
||||
// notify_active_to_idle().
|
||||
//
|
||||
// Returns true if a subsequent matching call to at() was reached.
|
||||
// Returns false if a collection cycle completed and idled
|
||||
// (notify_active_to_idle()) without reaching a matching at().
|
||||
//
|
||||
// precondition: Calling thread must be a Java thread.
|
||||
// precondition: Must not be a concurrent request or release operation.
|
||||
// precondition: is_controlled().
|
||||
// postcondition: is_controlled().
|
||||
static bool run_to(const char* breakpoint);
|
||||
|
||||
///////////
|
||||
// Notification functions, for use by the garbage collector.
|
||||
// Unless stated otherwise, all of these functions lock the monitor.
|
||||
// None of these functions safepoint.
|
||||
|
||||
// Indicates the concurrent collector has reached the designated point
|
||||
// in its execution. If a matching run_to() is active then notifies the
|
||||
// request and blocks until the request is cancelled.
|
||||
//
|
||||
// precondition: Calling thread must be a ConcurrentGC thread.
|
||||
// precondition: Must not be a concurrent notification.
|
||||
static void at(const char* breakpoint);
|
||||
|
||||
// Indicates the concurrent collector has completed a cycle. If there is
|
||||
// an active run_to_idle() request, it is notified of completion. If
|
||||
// there is an active run_to() request, it is replaced by a run_to_idle()
|
||||
// request, and notified of completion.
|
||||
//
|
||||
// precondition: Must not be a concurrent notification.
|
||||
static void notify_active_to_idle();
|
||||
|
||||
// Indicates a concurrent collection has been initiated. Does not lock
|
||||
// the monitor.
|
||||
//
|
||||
// precondition: Must not be a concurrent notification.
|
||||
// precondition: Must be at a safepoint or have the monitor locked.
|
||||
static void notify_idle_to_active();
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHARED_CONCURRENTGCBREAKPOINTS_HPP
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, 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/concurrentGCPhaseManager.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
|
||||
#define assert_ConcurrentGC_thread() \
|
||||
assert(Thread::current()->is_ConcurrentGC_thread(), "precondition")
|
||||
|
||||
#define assert_not_enter_unconstrained(phase) \
|
||||
assert((phase) != UNCONSTRAINED_PHASE, "Cannot enter \"unconstrained\" phase")
|
||||
|
||||
#define assert_manager_is_tos(manager, stack, kind) \
|
||||
assert((manager) == (stack)->_top, kind " manager is not top of stack")
|
||||
|
||||
ConcurrentGCPhaseManager::Stack::Stack() :
|
||||
_requested_phase(UNCONSTRAINED_PHASE),
|
||||
_top(NULL)
|
||||
{ }
|
||||
|
||||
ConcurrentGCPhaseManager::ConcurrentGCPhaseManager(int phase, Stack* stack) :
|
||||
_phase(phase),
|
||||
_active(true),
|
||||
_prev(NULL),
|
||||
_stack(stack)
|
||||
{
|
||||
assert_ConcurrentGC_thread();
|
||||
assert_not_enter_unconstrained(phase);
|
||||
assert(stack != NULL, "precondition");
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (stack->_top != NULL) {
|
||||
assert(stack->_top->_active, "precondition");
|
||||
_prev = stack->_top;
|
||||
}
|
||||
stack->_top = this;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
ConcurrentGCPhaseManager::~ConcurrentGCPhaseManager() {
|
||||
assert_ConcurrentGC_thread();
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_manager_is_tos(this, _stack, "This");
|
||||
wait_when_requested_impl();
|
||||
_stack->_top = _prev;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
bool ConcurrentGCPhaseManager::is_requested() const {
|
||||
assert_ConcurrentGC_thread();
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_manager_is_tos(this, _stack, "This");
|
||||
return _active && (_stack->_requested_phase == _phase);
|
||||
}
|
||||
|
||||
bool ConcurrentGCPhaseManager::wait_when_requested_impl() const {
|
||||
assert_ConcurrentGC_thread();
|
||||
assert_lock_strong(CGCPhaseManager_lock);
|
||||
bool waited = false;
|
||||
while (_active && (_stack->_requested_phase == _phase)) {
|
||||
waited = true;
|
||||
CGCPhaseManager_lock->wait_without_safepoint_check();
|
||||
}
|
||||
return waited;
|
||||
}
|
||||
|
||||
bool ConcurrentGCPhaseManager::wait_when_requested() const {
|
||||
assert_ConcurrentGC_thread();
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_manager_is_tos(this, _stack, "This");
|
||||
return wait_when_requested_impl();
|
||||
}
|
||||
|
||||
void ConcurrentGCPhaseManager::set_phase(int phase, bool force) {
|
||||
assert_ConcurrentGC_thread();
|
||||
assert_not_enter_unconstrained(phase);
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_manager_is_tos(this, _stack, "This");
|
||||
if (!force) wait_when_requested_impl();
|
||||
_phase = phase;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
void ConcurrentGCPhaseManager::deactivate() {
|
||||
assert_ConcurrentGC_thread();
|
||||
MonitorLocker ml(CGCPhaseManager_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_manager_is_tos(this, _stack, "This");
|
||||
_active = false;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
bool ConcurrentGCPhaseManager::wait_for_phase(int phase, Stack* stack) {
|
||||
assert(Thread::current()->is_Java_thread(), "precondition");
|
||||
assert(stack != NULL, "precondition");
|
||||
MonitorLocker ml(CGCPhaseManager_lock);
|
||||
// Update request and notify service of change.
|
||||
if (stack->_requested_phase != phase) {
|
||||
stack->_requested_phase = phase;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
if (phase == UNCONSTRAINED_PHASE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait until phase or IDLE is active.
|
||||
while (true) {
|
||||
bool idle = false;
|
||||
for (ConcurrentGCPhaseManager* manager = stack->_top;
|
||||
manager != NULL;
|
||||
manager = manager->_prev) {
|
||||
if (manager->_phase == phase) {
|
||||
return true; // phase is active.
|
||||
} else if (manager->_phase == IDLE_PHASE) {
|
||||
idle = true; // Note idle active, continue search for phase.
|
||||
}
|
||||
}
|
||||
if (idle) {
|
||||
return false; // idle is active and phase is not.
|
||||
} else {
|
||||
ml.wait(); // Wait for phase change.
|
||||
}
|
||||
}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, 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_SHARED_CONCURRENTGCPHASEMANAGER_HPP
|
||||
#define SHARE_GC_SHARED_CONCURRENTGCPHASEMANAGER_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
// Manage concurrent phase information, to support WhiteBox testing.
|
||||
// Managers are stack allocated. Managers may be nested, to support
|
||||
// nested subphases.
|
||||
class ConcurrentGCPhaseManager : public StackObj {
|
||||
public:
|
||||
|
||||
// Special phase ids used by all GC's that use this facility.
|
||||
static const int UNCONSTRAINED_PHASE = 0; // Unconstrained or no request.
|
||||
static const int IDLE_PHASE = 1; // Concurrent processing is idle.
|
||||
|
||||
// Stack of phase managers.
|
||||
class Stack {
|
||||
friend class ConcurrentGCPhaseManager;
|
||||
|
||||
public:
|
||||
// Create an empty stack of phase managers.
|
||||
Stack();
|
||||
|
||||
private:
|
||||
int _requested_phase;
|
||||
ConcurrentGCPhaseManager* _top;
|
||||
|
||||
// Non-copyable - never defined.
|
||||
Stack(const Stack&);
|
||||
Stack& operator=(const Stack&);
|
||||
};
|
||||
|
||||
// Construct and push a new manager on the stack, activating phase.
|
||||
// Notifies callers in wait_for_phase of the phase change.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - phase != UNCONSTRAINED_PHASE
|
||||
// - stack != NULL
|
||||
// - other managers on stack must all be active.
|
||||
ConcurrentGCPhaseManager(int phase, Stack* stack);
|
||||
|
||||
// Pop this manager off the stack, deactivating phase. Before
|
||||
// changing phases, if is_requested() is true, wait until the
|
||||
// request is changed. After changing phases, notifies callers of
|
||||
// wait_for_phase of the phase change.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - this must be the current top of the manager stack
|
||||
~ConcurrentGCPhaseManager();
|
||||
|
||||
// Returns true if this phase is active and is currently requested.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - this must be the current top of manager stack
|
||||
bool is_requested() const;
|
||||
|
||||
// Wait until is_requested() is false. Returns true if waited.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - this must be the current top of manager stack
|
||||
bool wait_when_requested() const;
|
||||
|
||||
// Directly step from one phase to another, without needing to pop a
|
||||
// manager from the stack and allocate a new one. Before changing
|
||||
// phases, if is_requested() is true and force is false, wait until
|
||||
// the request is changed. After changing phases, notifies callers
|
||||
// of wait_for_phase of the phase change.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - phase != UNCONSTRAINED_PHASE
|
||||
// - this must be the current top of manager stack
|
||||
void set_phase(int phase, bool force);
|
||||
|
||||
// Deactivate the manager. An inactive manager no longer blocks
|
||||
// transitions out of the associated phase when that phase has been
|
||||
// requested.
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a ConcurrentGC thread
|
||||
// - this must be the current top of manager stack
|
||||
void deactivate();
|
||||
|
||||
// Used to implement CollectedHeap::request_concurrent_phase().
|
||||
// Updates request to the new phase, and notifies threads blocked on
|
||||
// the old request of the change. Returns true if the phase is
|
||||
// UNCONSTRAINED_PHASE. Otherwise, waits until an active phase is
|
||||
// the requested phase (returning true) or IDLE_PHASE (returning
|
||||
// false if not also the requested phase).
|
||||
//
|
||||
// Preconditions:
|
||||
// - Calling thread must be a Java thread
|
||||
// - stack must be non-NULL
|
||||
static bool wait_for_phase(int phase, Stack* stack);
|
||||
|
||||
private:
|
||||
int _phase;
|
||||
bool _active;
|
||||
ConcurrentGCPhaseManager* _prev;
|
||||
Stack* _stack;
|
||||
|
||||
// Non-copyable - never defined.
|
||||
ConcurrentGCPhaseManager(const ConcurrentGCPhaseManager&);
|
||||
ConcurrentGCPhaseManager& operator=(const ConcurrentGCPhaseManager&);
|
||||
|
||||
bool wait_when_requested_impl() const;
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHARED_CONCURRENTGCPHASEMANAGER_HPP
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2020, 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
|
||||
@ -60,6 +60,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
|
||||
case _wb_full_gc:
|
||||
return "WhiteBox Initiated Full GC";
|
||||
|
||||
case _wb_breakpoint:
|
||||
return "WhiteBox Initiated Run to Breakpoint";
|
||||
|
||||
case _archive_time_gc:
|
||||
return "Full GC for -Xshare:dump";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2020, 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
|
||||
@ -52,6 +52,7 @@ class GCCause : public AllStatic {
|
||||
_wb_young_gc,
|
||||
_wb_conc_mark,
|
||||
_wb_full_gc,
|
||||
_wb_breakpoint,
|
||||
_archive_time_gc,
|
||||
|
||||
/* implementation independent, but reserved for GC use */
|
||||
|
59
src/hotspot/share/gc/z/zBreakpoint.cpp
Normal file
59
src/hotspot/share/gc/z/zBreakpoint.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/z/zBreakpoint.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
bool ZBreakpoint::_start_gc = false;
|
||||
|
||||
void ZBreakpoint::start_gc() {
|
||||
MonitorLocker ml(ConcurrentGCBreakpoints::monitor());
|
||||
assert(ConcurrentGCBreakpoints::is_controlled(), "Invalid state");
|
||||
assert(!_start_gc, "Invalid state");
|
||||
_start_gc = true;
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
void ZBreakpoint::at_before_gc() {
|
||||
MonitorLocker ml(ConcurrentGCBreakpoints::monitor(), Mutex::_no_safepoint_check_flag);
|
||||
while (ConcurrentGCBreakpoints::is_controlled() && !_start_gc) {
|
||||
ml.wait();
|
||||
}
|
||||
_start_gc = false;
|
||||
ConcurrentGCBreakpoints::notify_idle_to_active();
|
||||
}
|
||||
|
||||
void ZBreakpoint::at_after_gc() {
|
||||
ConcurrentGCBreakpoints::notify_active_to_idle();
|
||||
}
|
||||
|
||||
void ZBreakpoint::at_after_marking_started() {
|
||||
ConcurrentGCBreakpoints::at("AFTER MARKING STARTED");
|
||||
}
|
||||
|
||||
void ZBreakpoint::at_before_marking_completed() {
|
||||
ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED");
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -21,30 +21,22 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
#ifndef SHARE_GC_Z_ZBREAKPOINT_HPP
|
||||
#define SHARE_GC_Z_ZBREAKPOINT_HPP
|
||||
|
||||
/*
|
||||
* @test TestConcurrentPhaseControlG1Basics
|
||||
* @bug 8169517
|
||||
* @requires vm.gc.G1
|
||||
* @summary Verify G1 supports concurrent phase control.
|
||||
* @key gc
|
||||
* @modules java.base
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -XX:+UseG1GC
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* gc.concurrent_phase_control.TestConcurrentPhaseControlG1Basics
|
||||
*/
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
import gc.concurrent_phase_control.CheckSupported;
|
||||
class ZBreakpoint : public AllStatic {
|
||||
private:
|
||||
static bool _start_gc;
|
||||
|
||||
public class TestConcurrentPhaseControlG1Basics {
|
||||
public:
|
||||
static void start_gc();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
CheckSupported.check("G1");
|
||||
}
|
||||
}
|
||||
static void at_before_gc();
|
||||
static void at_after_gc();
|
||||
static void at_after_marking_started();
|
||||
static void at_before_marking_completed();
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZBREAKPOINT_HPP
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2020, 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
|
||||
@ -353,3 +353,7 @@ void ZCollectedHeap::verify(VerifyOption option /* ignored */) {
|
||||
bool ZCollectedHeap::is_oop(oop object) const {
|
||||
return _heap.is_oop(ZOop::to_address(object));
|
||||
}
|
||||
|
||||
bool ZCollectedHeap::supports_concurrent_gc_breakpoints() const {
|
||||
return true;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2020, 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
|
||||
@ -127,6 +127,7 @@ public:
|
||||
virtual void prepare_for_verify();
|
||||
virtual void verify(VerifyOption option /* ignored */);
|
||||
virtual bool is_oop(oop object) const;
|
||||
virtual bool supports_concurrent_gc_breakpoints() const;
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZCOLLECTEDHEAP_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2020, 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,6 +25,7 @@
|
||||
#include "gc/shared/gcId.hpp"
|
||||
#include "gc/shared/gcLocker.hpp"
|
||||
#include "gc/shared/isGCActiveMark.hpp"
|
||||
#include "gc/z/zBreakpoint.hpp"
|
||||
#include "gc/z/zCollectedHeap.hpp"
|
||||
#include "gc/z/zDriver.hpp"
|
||||
#include "gc/z/zHeap.inline.hpp"
|
||||
@ -260,6 +261,11 @@ void ZDriver::collect(GCCause::Cause cause) {
|
||||
_gc_locker_port.signal();
|
||||
break;
|
||||
|
||||
case GCCause::_wb_breakpoint:
|
||||
ZBreakpoint::start_gc();
|
||||
_gc_cycle_port.send_async(cause);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Other causes not supported
|
||||
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
|
||||
@ -292,7 +298,9 @@ void ZDriver::pause_mark_start() {
|
||||
|
||||
void ZDriver::concurrent_mark() {
|
||||
ZStatTimer timer(ZPhaseConcurrentMark);
|
||||
ZBreakpoint::at_after_marking_started();
|
||||
ZHeap::heap()->mark(true /* initial */);
|
||||
ZBreakpoint::at_before_marking_completed();
|
||||
}
|
||||
|
||||
bool ZDriver::pause_mark_end() {
|
||||
@ -417,6 +425,8 @@ void ZDriver::run_service() {
|
||||
continue;
|
||||
}
|
||||
|
||||
ZBreakpoint::at_before_gc();
|
||||
|
||||
// Run GC
|
||||
gc(cause);
|
||||
|
||||
@ -425,6 +435,8 @@ void ZDriver::run_service() {
|
||||
|
||||
// Check for out of memory condition
|
||||
check_out_of_memory();
|
||||
|
||||
ZBreakpoint::at_after_gc();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "compiler/compilationPolicy.hpp"
|
||||
#include "compiler/methodMatcher.hpp"
|
||||
#include "compiler/directivesParser.hpp"
|
||||
#include "gc/shared/concurrentGCBreakpoints.hpp"
|
||||
#include "gc/shared/gcConfig.hpp"
|
||||
#include "gc/shared/genArguments.hpp"
|
||||
#include "gc/shared/genCollectedHeap.hpp"
|
||||
@ -394,15 +395,27 @@ WB_ENTRY(jlong, WB_GetHeapAlignment(JNIEnv* env, jobject o))
|
||||
return (jlong)HeapAlignment;
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(jboolean, WB_SupportsConcurrentGCPhaseControl(JNIEnv* env, jobject o))
|
||||
return Universe::heap()->supports_concurrent_phase_control();
|
||||
WB_ENTRY(jboolean, WB_SupportsConcurrentGCBreakpoints(JNIEnv* env, jobject o))
|
||||
return Universe::heap()->supports_concurrent_gc_breakpoints();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(jboolean, WB_RequestConcurrentGCPhase(JNIEnv* env, jobject o, jstring name))
|
||||
Handle h_name(THREAD, JNIHandles::resolve(name));
|
||||
WB_ENTRY(void, WB_ConcurrentGCAcquireControl(JNIEnv* env, jobject o))
|
||||
ConcurrentGCBreakpoints::acquire_control();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_ConcurrentGCReleaseControl(JNIEnv* env, jobject o))
|
||||
ConcurrentGCBreakpoints::release_control();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_ConcurrentGCRunToIdle(JNIEnv* env, jobject o))
|
||||
ConcurrentGCBreakpoints::run_to_idle();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(jboolean, WB_ConcurrentGCRunTo(JNIEnv* env, jobject o, jobject at))
|
||||
Handle h_name(THREAD, JNIHandles::resolve(at));
|
||||
ResourceMark rm;
|
||||
const char* c_name = java_lang_String::as_utf8_string(h_name());
|
||||
return Universe::heap()->request_concurrent_phase(c_name);
|
||||
return ConcurrentGCBreakpoints::run_to(c_name);
|
||||
WB_END
|
||||
|
||||
#if INCLUDE_G1GC
|
||||
@ -2440,9 +2453,12 @@ static JNINativeMethod methods[] = {
|
||||
{CC"isGCSupported", CC"(I)Z", (void*)&WB_IsGCSupported},
|
||||
{CC"isGCSelected", CC"(I)Z", (void*)&WB_IsGCSelected},
|
||||
{CC"isGCSelectedErgonomically", CC"()Z", (void*)&WB_IsGCSelectedErgonomically},
|
||||
{CC"supportsConcurrentGCPhaseControl", CC"()Z", (void*)&WB_SupportsConcurrentGCPhaseControl},
|
||||
{CC"requestConcurrentGCPhase0", CC"(Ljava/lang/String;)Z",
|
||||
(void*)&WB_RequestConcurrentGCPhase},
|
||||
{CC"supportsConcurrentGCBreakpoints", CC"()Z", (void*)&WB_SupportsConcurrentGCBreakpoints},
|
||||
{CC"concurrentGCAcquireControl0", CC"()V", (void*)&WB_ConcurrentGCAcquireControl},
|
||||
{CC"concurrentGCReleaseControl0", CC"()V", (void*)&WB_ConcurrentGCReleaseControl},
|
||||
{CC"concurrentGCRunToIdle0", CC"()V", (void*)&WB_ConcurrentGCRunToIdle},
|
||||
{CC"concurrentGCRunTo0", CC"(Ljava/lang/String;)Z",
|
||||
(void*)&WB_ConcurrentGCRunTo},
|
||||
{CC"checkLibSpecifiesNoexecstack", CC"(Ljava/lang/String;)Z",
|
||||
(void*)&WB_CheckLibSpecifiesNoexecstack},
|
||||
{CC"isContainerized", CC"()Z", (void*)&WB_IsContainerized },
|
||||
|
@ -78,7 +78,7 @@ Mutex* MarkStackFreeList_lock = NULL;
|
||||
Mutex* MarkStackChunkList_lock = NULL;
|
||||
Mutex* MonitoringSupport_lock = NULL;
|
||||
Mutex* ParGCRareEvent_lock = NULL;
|
||||
Monitor* CGCPhaseManager_lock = NULL;
|
||||
Monitor* ConcurrentGCBreakpoints_lock = NULL;
|
||||
Mutex* Compile_lock = NULL;
|
||||
Monitor* MethodCompileQueue_lock = NULL;
|
||||
Monitor* CompileThread_lock = NULL;
|
||||
@ -229,7 +229,6 @@ void mutex_init() {
|
||||
def(StringDedupTable_lock , PaddedMutex , leaf + 1, true, _safepoint_check_never);
|
||||
}
|
||||
def(ParGCRareEvent_lock , PaddedMutex , leaf , true, _safepoint_check_always);
|
||||
def(CGCPhaseManager_lock , PaddedMonitor, leaf, false, _safepoint_check_always);
|
||||
def(CodeCache_lock , PaddedMonitor, special, true, _safepoint_check_never);
|
||||
def(RawMonitor_lock , PaddedMutex , special, true, _safepoint_check_never);
|
||||
def(OopMapCacheAlloc_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for oop_map_cache allocation.
|
||||
@ -295,6 +294,7 @@ void mutex_init() {
|
||||
def(JvmtiThreadState_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); // Used by JvmtiThreadState/JvmtiEventController
|
||||
def(Management_lock , PaddedMutex , nonleaf+2, false, _safepoint_check_always); // used for JVM management
|
||||
|
||||
def(ConcurrentGCBreakpoints_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
||||
def(Compile_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always);
|
||||
def(MethodData_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always);
|
||||
def(TouchedMethodLog_lock , PaddedMutex , nonleaf+3, false, _safepoint_check_always);
|
||||
|
@ -57,7 +57,6 @@ extern Monitor* CodeCache_lock; // a lock on the CodeCache, ran
|
||||
extern Mutex* MethodData_lock; // a lock on installation of method data
|
||||
extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info
|
||||
extern Mutex* RetData_lock; // a lock on installation of RetData inside method data
|
||||
extern Monitor* CGCPhaseManager_lock; // a lock to protect a concurrent GC's phase management
|
||||
extern Monitor* VMOperationQueue_lock; // a lock on queue of vm_operations waiting to execute
|
||||
extern Monitor* VMOperationRequest_lock; // a lock on Threads waiting for a vm_operation to terminate
|
||||
extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads
|
||||
@ -76,6 +75,7 @@ extern Mutex* MarkStackFreeList_lock; // Protects access to the globa
|
||||
extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list.
|
||||
extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools.
|
||||
extern Mutex* ParGCRareEvent_lock; // Synchronizes various (rare) parallel GC ops.
|
||||
extern Monitor* ConcurrentGCBreakpoints_lock; // Protects concurrent GC breakpoint management
|
||||
extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
|
||||
extern Monitor* MethodCompileQueue_lock; // a lock held when method compilations are enqueued, dequeued
|
||||
extern Monitor* CompileThread_lock; // a lock held by compile threads during compilation system initialization
|
||||
|
148
test/hotspot/jtreg/gc/TestConcurrentGCBreakpoints.java
Normal file
148
test/hotspot/jtreg/gc/TestConcurrentGCBreakpoints.java
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
package gc;
|
||||
|
||||
/*
|
||||
* @test TestConcurrentGCBreakpoints
|
||||
* @summary Test of WhiteBox concurrent GC control.
|
||||
* @key gc
|
||||
* @modules java.base
|
||||
* @library /test/lib
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* gc.TestConcurrentGCBreakpoints
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import sun.hotspot.gc.GC;
|
||||
|
||||
public class TestConcurrentGCBreakpoints {
|
||||
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
// All testN() assume initial state is idle, and restore that state.
|
||||
|
||||
// Step through the common breakpoints.
|
||||
private static void testSimpleCycle() throws Exception {
|
||||
System.out.println("testSimpleCycle");
|
||||
try {
|
||||
// Run one cycle.
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
|
||||
WB.concurrentGCRunToIdle();
|
||||
// Run a second cycle.
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
|
||||
WB.concurrentGCRunToIdle();
|
||||
} finally {
|
||||
WB.concurrentGCRunToIdle();
|
||||
}
|
||||
}
|
||||
|
||||
// Verify attempted wraparound detected and reported.
|
||||
private static void testEndBeforeBreakpointError() throws Exception {
|
||||
System.out.println("testEndBeforeBreakpointError");
|
||||
try {
|
||||
WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
|
||||
try {
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
} catch (IllegalStateException e) {
|
||||
// Reached end of cycle before desired breakpoint.
|
||||
}
|
||||
} finally {
|
||||
WB.concurrentGCRunToIdle();
|
||||
}
|
||||
}
|
||||
|
||||
// Verify attempted wraparound detected and reported without throw.
|
||||
private static void testEndBeforeBreakpoint() throws Exception {
|
||||
System.out.println("testEndBeforeBreakpoint");
|
||||
try {
|
||||
WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
|
||||
if (WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED, false)) {
|
||||
throw new RuntimeException("Unexpected wraparound");
|
||||
}
|
||||
} finally {
|
||||
WB.concurrentGCRunToIdle();
|
||||
}
|
||||
}
|
||||
|
||||
private static void testUnknownBreakpoint() throws Exception {
|
||||
System.out.println("testUnknownBreakpoint");
|
||||
try {
|
||||
if (WB.concurrentGCRunTo("UNKNOWN BREAKPOINT", false)) {
|
||||
throw new RuntimeException("RunTo UNKNOWN BREAKPOINT");
|
||||
}
|
||||
} finally {
|
||||
WB.concurrentGCRunToIdle();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test() throws Exception {
|
||||
try {
|
||||
System.out.println("taking control");
|
||||
WB.concurrentGCAcquireControl();
|
||||
testSimpleCycle();
|
||||
testEndBeforeBreakpointError();
|
||||
testEndBeforeBreakpoint();
|
||||
testUnknownBreakpoint();
|
||||
} finally {
|
||||
System.out.println("releasing control");
|
||||
WB.concurrentGCReleaseControl();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean expectSupported() {
|
||||
return GC.G1.isSelected() ||
|
||||
GC.Z.isSelected();
|
||||
}
|
||||
|
||||
private static boolean expectUnsupported() {
|
||||
return GC.Serial.isSelected() ||
|
||||
GC.Parallel.isSelected() ||
|
||||
GC.Epsilon.isSelected() ||
|
||||
GC.Shenandoah.isSelected();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean supported = WB.supportsConcurrentGCBreakpoints();
|
||||
if (expectSupported()) {
|
||||
if (supported) {
|
||||
test();
|
||||
} else {
|
||||
throw new RuntimeException("Expected support");
|
||||
}
|
||||
} else if (expectUnsupported()) {
|
||||
if (supported) {
|
||||
throw new RuntimeException("Unexpected support");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unknown GC");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020, 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
|
||||
@ -21,15 +21,14 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package gc.g1.TestJNIWeakG1;
|
||||
package gc.TestJNIWeak;
|
||||
|
||||
/* @test
|
||||
* @bug 8166188 8178813
|
||||
* @summary Test return of JNI weak global refs during concurrent
|
||||
* marking, verifying the use of the G1 load barrier to keep the
|
||||
* marking, verifying the use of the load barrier to keep the
|
||||
* referent alive.
|
||||
* @key gc
|
||||
* @requires vm.gc.G1
|
||||
* @modules java.base
|
||||
* @library /test/lib
|
||||
* @build sun.hotspot.WhiteBox
|
||||
@ -38,25 +37,24 @@ package gc.g1.TestJNIWeakG1;
|
||||
* @run main/othervm/native
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:+UseG1GC -XX:MaxTenuringThreshold=2
|
||||
* -Xint
|
||||
* gc.g1.TestJNIWeakG1.TestJNIWeakG1
|
||||
* gc.TestJNIWeak.TestJNIWeak
|
||||
* @run main/othervm/native
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:+UseG1GC -XX:MaxTenuringThreshold=2
|
||||
* -Xcomp
|
||||
* gc.g1.TestJNIWeakG1.TestJNIWeakG1
|
||||
* gc.TestJNIWeak.TestJNIWeak
|
||||
*/
|
||||
|
||||
import sun.hotspot.gc.GC;
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
public final class TestJNIWeakG1 {
|
||||
public final class TestJNIWeak {
|
||||
|
||||
static {
|
||||
System.loadLibrary("TestJNIWeakG1");
|
||||
System.loadLibrary("TestJNIWeak");
|
||||
}
|
||||
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
@ -82,7 +80,7 @@ public final class TestJNIWeakG1 {
|
||||
// native call return value handling path (when resolve is false).
|
||||
private boolean resolve = true;
|
||||
|
||||
TestJNIWeakG1(boolean resolve) {
|
||||
TestJNIWeak(boolean resolve) {
|
||||
this.resolve = resolve;
|
||||
}
|
||||
|
||||
@ -107,10 +105,10 @@ public final class TestJNIWeakG1 {
|
||||
testObject = null;
|
||||
}
|
||||
|
||||
// Repeatedly perform young-only GC until o is in the old generation.
|
||||
// Repeatedly perform full GC until o is in the old generation.
|
||||
private void gcUntilOld(Object o) {
|
||||
while (!WB.isObjectInOldGen(o)) {
|
||||
WB.youngGC();
|
||||
WB.fullGC();
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +129,7 @@ public final class TestJNIWeakG1 {
|
||||
System.out.println("running checkSanity");
|
||||
try {
|
||||
// Inhibit concurrent GC during this check.
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCAcquireControl();
|
||||
|
||||
int value = 5;
|
||||
try {
|
||||
@ -142,8 +140,7 @@ public final class TestJNIWeakG1 {
|
||||
}
|
||||
|
||||
} finally {
|
||||
// Remove request.
|
||||
WB.requestConcurrentGCPhase("ANY");
|
||||
WB.concurrentGCReleaseControl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,16 +154,16 @@ public final class TestJNIWeakG1 {
|
||||
checkValue(value);
|
||||
gcUntilOld(testObject);
|
||||
// Run a concurrent collection after object is old.
|
||||
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCAcquireControl();
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
WB.concurrentGCRunToIdle();
|
||||
// Verify weak ref still has expected value.
|
||||
checkValue(value);
|
||||
} finally {
|
||||
forget();
|
||||
}
|
||||
} finally {
|
||||
// Remove request.
|
||||
WB.requestConcurrentGCPhase("ANY");
|
||||
WB.concurrentGCReleaseControl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,13 +177,14 @@ public final class TestJNIWeakG1 {
|
||||
checkValue(value);
|
||||
gcUntilOld(testObject);
|
||||
// Run a concurrent collection after object is old.
|
||||
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCAcquireControl();
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
WB.concurrentGCRunToIdle();
|
||||
checkValue(value);
|
||||
testObject = null;
|
||||
// Run a concurrent collection after strong ref removed.
|
||||
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCRunTo(WB.AFTER_MARKING_STARTED);
|
||||
WB.concurrentGCRunToIdle();
|
||||
// Verify weak ref cleared as expected.
|
||||
Object recorded = getObject();
|
||||
if (recorded != null) {
|
||||
@ -196,8 +194,7 @@ public final class TestJNIWeakG1 {
|
||||
forget();
|
||||
}
|
||||
} finally {
|
||||
// Remove request.
|
||||
WB.requestConcurrentGCPhase("ANY");
|
||||
WB.concurrentGCReleaseControl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,11 +209,11 @@ public final class TestJNIWeakG1 {
|
||||
checkValue(value);
|
||||
gcUntilOld(testObject);
|
||||
// Block concurrent cycle until we're ready.
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCAcquireControl();
|
||||
checkValue(value);
|
||||
testObject = null; // Discard strong ref
|
||||
// Run through mark_from_roots.
|
||||
WB.requestConcurrentGCPhase("BEFORE_REMARK");
|
||||
// Run through most of marking
|
||||
WB.concurrentGCRunTo(WB.BEFORE_MARKING_COMPLETED);
|
||||
// Fetch weak ref'ed object. Should be kept alive now.
|
||||
Object recovered = getObject();
|
||||
if (recovered == null) {
|
||||
@ -224,7 +221,7 @@ public final class TestJNIWeakG1 {
|
||||
}
|
||||
// Finish collection, including reference processing.
|
||||
// Block any further cycles while we finish check.
|
||||
WB.requestConcurrentGCPhase("IDLE");
|
||||
WB.concurrentGCRunToIdle();
|
||||
// Fetch weak ref'ed object. Referent is manifestly
|
||||
// live in recovered; the earlier fetch should have
|
||||
// kept it alive through collection, so weak ref
|
||||
@ -241,8 +238,7 @@ public final class TestJNIWeakG1 {
|
||||
forget();
|
||||
}
|
||||
} finally {
|
||||
// Remove request.
|
||||
WB.requestConcurrentGCPhase("ANY");
|
||||
WB.concurrentGCReleaseControl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,13 +251,18 @@ public final class TestJNIWeakG1 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (!WB.supportsConcurrentGCBreakpoints()) {
|
||||
throw new SkippedException(
|
||||
GC.selected().name() + " doesn't support concurrent GC breakpoints");
|
||||
}
|
||||
|
||||
// Perform check with direct jweak resolution.
|
||||
System.out.println("Check with jweak resolved");
|
||||
new TestJNIWeakG1(true).check();
|
||||
new TestJNIWeak(true).check();
|
||||
|
||||
// Perform check with implicit jweak resolution by native
|
||||
// call's return value handling.
|
||||
System.out.println("Check with jweak returned");
|
||||
new TestJNIWeakG1(false).check();
|
||||
new TestJNIWeak(false).check();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020, 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
|
||||
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Native support for TestJNIWeakG1 test.
|
||||
* Native support for TestJNIWeak test.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
@ -30,13 +30,13 @@
|
||||
static jweak registered = NULL;
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_gc_g1_TestJNIWeakG1_TestJNIWeakG1_registerObject(JNIEnv* env, jclass jclazz, jobject value) {
|
||||
Java_gc_TestJNIWeak_TestJNIWeak_registerObject(JNIEnv* env, jclass jclazz, jobject value) {
|
||||
// assert registered == NULL
|
||||
registered = (*env)->NewWeakGlobalRef(env, value);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_gc_g1_TestJNIWeakG1_TestJNIWeakG1_unregisterObject(JNIEnv* env, jclass jclazz) {
|
||||
Java_gc_TestJNIWeak_TestJNIWeak_unregisterObject(JNIEnv* env, jclass jclazz) {
|
||||
if (registered != NULL) {
|
||||
(*env)->DeleteWeakGlobalRef(env, registered);
|
||||
registered = NULL;
|
||||
@ -45,14 +45,14 @@ Java_gc_g1_TestJNIWeakG1_TestJNIWeakG1_unregisterObject(JNIEnv* env, jclass jcla
|
||||
|
||||
// Directly return jweak, to be resolved by native call's return value handling.
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_gc_g1_TestJNIWeakG1_TestJNIWeakG1_getReturnedWeak(JNIEnv* env, jclass jclazz) {
|
||||
Java_gc_TestJNIWeak_TestJNIWeak_getReturnedWeak(JNIEnv* env, jclass jclazz) {
|
||||
// assert registered != NULL
|
||||
return registered;
|
||||
}
|
||||
|
||||
// Directly resolve jweak and return the result.
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_gc_g1_TestJNIWeakG1_TestJNIWeakG1_getResolvedWeak(JNIEnv* env, jclass jclazz) {
|
||||
Java_gc_TestJNIWeak_TestJNIWeak_getResolvedWeak(JNIEnv* env, jclass jclazz) {
|
||||
// assert registered != NULL
|
||||
return (*env)->NewLocalRef(env, registered);
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* Utility class that uses the WhiteBox concurrent GC phase control to
|
||||
* step through a provided sequence of phases, and verify that the
|
||||
* phases were actually reached as expected.
|
||||
*
|
||||
* To use:
|
||||
*
|
||||
* (1) The main test class has a main function which calls this helper
|
||||
* class's check() function with appropriate arguments for the
|
||||
* collector being tested.
|
||||
*
|
||||
* (2) The test program must provide access to WhiteBox, as it is used
|
||||
* by this support class.
|
||||
*
|
||||
* (4) The main test class should be invoked as a driver. This
|
||||
* helper class's check() function will run its Executor class in a
|
||||
* subprocess, in order to capture its output for analysis.
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public final class CheckControl {
|
||||
// gcName: The name of the GC, logged as "Using <name>" near the
|
||||
// beginning of the log output.
|
||||
//
|
||||
// gcOptions: Command line options for invoking the desired
|
||||
// collector and logging options to produce output that can be
|
||||
// matched against the regex patterns in the gcPhaseInfo pairs.
|
||||
//
|
||||
// gcPhaseInfo: An array of pairs of strings. Each pair is a
|
||||
// phase name and a regex pattern for recognizing the associated
|
||||
// log message. The regex pattern can be null if no log message
|
||||
// is associated with the named phase. The test will iterate
|
||||
// through the array, requesting each phase in turn.
|
||||
public static void check(String gcName,
|
||||
String[] gcOptions,
|
||||
String[][] gcPhaseInfo) throws Exception {
|
||||
String[] stepPhases = new String[gcPhaseInfo.length];
|
||||
for (int i = 0; i < gcPhaseInfo.length; ++i) {
|
||||
stepPhases[i] = gcPhaseInfo[i][0];
|
||||
}
|
||||
String messages = executeTest(gcName, gcOptions, stepPhases);
|
||||
checkPhaseControl(messages, gcPhaseInfo);
|
||||
}
|
||||
|
||||
private static void fail(String message) throws Exception {
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
|
||||
private static final String requestPrefix = "Requesting concurrent phase: ";
|
||||
private static final String reachedPrefix = "Reached concurrent phase: ";
|
||||
|
||||
private static String executeTest(String gcName,
|
||||
String[] gcOptions,
|
||||
String[] gcStepPhases) throws Exception {
|
||||
System.out.println("\n---------- Testing ---------");
|
||||
|
||||
final String[] wb_arguments = {
|
||||
"-Xbootclasspath/a:.",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:+WhiteBoxAPI"
|
||||
};
|
||||
|
||||
List<String> arglist = new ArrayList<String>();
|
||||
Collections.addAll(arglist, wb_arguments);
|
||||
Collections.addAll(arglist, gcOptions);
|
||||
arglist.add(Executor.class.getName());
|
||||
Collections.addAll(arglist, gcStepPhases);
|
||||
String[] arguments = arglist.toArray(new String[arglist.size()]);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(arguments);
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
|
||||
String messages = output.getStdout();
|
||||
System.out.println(messages);
|
||||
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain("Using " + gcName);
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private static void checkPhaseControl(String messages,
|
||||
String[][] gcPhaseInfo)
|
||||
throws Exception
|
||||
{
|
||||
// Iterate through the phase sequence for the test, verifying
|
||||
// output contains appropriate sequences of request message,
|
||||
// log message for phase, and request reached message. Note
|
||||
// that a log message for a phase may occur later than the
|
||||
// associated request reached message, or even the following
|
||||
// request message.
|
||||
|
||||
Pattern nextReqP = Pattern.compile(requestPrefix);
|
||||
Matcher nextReqM = nextReqP.matcher(messages);
|
||||
|
||||
Pattern nextReachP = Pattern.compile(reachedPrefix);
|
||||
Matcher nextReachM = nextReachP.matcher(messages);
|
||||
|
||||
String pendingPhaseMessage = null;
|
||||
int pendingPhaseMessagePosition = -1;
|
||||
|
||||
int position = 0;
|
||||
for (String[] phase: gcPhaseInfo) {
|
||||
String phaseName = phase[0];
|
||||
String phaseMsg = phase[1];
|
||||
|
||||
System.out.println("Checking phase " + phaseName);
|
||||
|
||||
// Update the "next" matchers to refer to the next
|
||||
// corresponding pair of request and reached messages.
|
||||
if (!nextReqM.find()) {
|
||||
fail("Didn't find next phase request");
|
||||
} else if ((position != 0) && (nextReqM.start() < nextReachM.end())) {
|
||||
fail("Next request before previous reached");
|
||||
} else if (!nextReachM.find()) {
|
||||
fail("Didn't find next phase reached");
|
||||
} else if (nextReachM.start() <= nextReqM.end()) {
|
||||
fail("Next request/reached misordered");
|
||||
}
|
||||
|
||||
// Find the expected request message, and ensure it is the next.
|
||||
Pattern reqP = Pattern.compile(requestPrefix + phaseName);
|
||||
Matcher reqM = reqP.matcher(messages);
|
||||
if (!reqM.find(position)) {
|
||||
fail("Didn't find request for " + phaseName);
|
||||
} else if (reqM.start() != nextReqM.start()) {
|
||||
fail("Request mis-positioned for " + phaseName);
|
||||
}
|
||||
|
||||
// Find the expected reached message, and ensure it is the next.
|
||||
Pattern reachP = Pattern.compile(reachedPrefix + phaseName);
|
||||
Matcher reachM = reachP.matcher(messages);
|
||||
if (!reachM.find(position)) {
|
||||
fail("Didn't find reached for " + phaseName);
|
||||
} else if (reachM.start() != nextReachM.start()) {
|
||||
fail("Reached mis-positioned for " + phaseName);
|
||||
}
|
||||
|
||||
// If there is a pending log message (see below), ensure
|
||||
// it was before the current reached message.
|
||||
if (pendingPhaseMessage != null) {
|
||||
if (pendingPhaseMessagePosition >= reachM.start()) {
|
||||
fail("Log message after next reached message: " +
|
||||
pendingPhaseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// If the phase has an associated logging message, verify
|
||||
// such a logging message is present following the
|
||||
// request, and otherwise positioned appropriately. The
|
||||
// complication here is that the logging message
|
||||
// associated with a request might follow the reached
|
||||
// message, and even the next request message, if there is
|
||||
// a later request. But it must preceed the next
|
||||
// logging message and the next reached message.
|
||||
boolean clearPendingPhaseMessage = true;
|
||||
if (phaseMsg != null) {
|
||||
Pattern logP = Pattern.compile("GC\\(\\d+\\)\\s+" + phaseMsg);
|
||||
Matcher logM = logP.matcher(messages);
|
||||
if (!logM.find(reqM.end())) {
|
||||
fail("Didn't find message " + phaseMsg);
|
||||
}
|
||||
|
||||
if (pendingPhaseMessage != null) {
|
||||
if (pendingPhaseMessagePosition >= logM.start()) {
|
||||
fail("Log messages out of order: " +
|
||||
pendingPhaseMessage + " should preceed " +
|
||||
phaseMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (reachM.end() <= logM.start()) {
|
||||
clearPendingPhaseMessage = false;
|
||||
pendingPhaseMessage = phaseMsg;
|
||||
pendingPhaseMessagePosition = logM.end();
|
||||
}
|
||||
}
|
||||
if (clearPendingPhaseMessage) {
|
||||
pendingPhaseMessage = null;
|
||||
pendingPhaseMessagePosition = -1;
|
||||
}
|
||||
|
||||
// Update position for start of next phase search.
|
||||
position = reachM.end();
|
||||
}
|
||||
// It's okay for there to be a leftover pending phase message.
|
||||
// We know it was found before the end of the log.
|
||||
}
|
||||
|
||||
private static final class Executor {
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
private static void step(String phase) {
|
||||
System.out.println(requestPrefix + phase);
|
||||
WB.requestConcurrentGCPhase(phase);
|
||||
System.out.println(reachedPrefix + phase);
|
||||
}
|
||||
|
||||
public static void main(String[] phases) throws Exception {
|
||||
// Iterate through set sequence of phases, reporting each.
|
||||
for (String phase: phases) {
|
||||
step(phase);
|
||||
}
|
||||
// Wait a little to allow a delayed logging message for
|
||||
// the final request/reached to be printed before exiting
|
||||
// the program.
|
||||
Thread.sleep(250);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* Utility class that provides verification of expected behavior of
|
||||
* the Concurrent GC Phase Control WB API when the current GC supports
|
||||
* phase control. The invoking test must provide WhiteBox access.
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
public class CheckSupported {
|
||||
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
public static void check(String gcName) throws Exception {
|
||||
// Verify supported.
|
||||
if (!WB.supportsConcurrentGCPhaseControl()) {
|
||||
throw new RuntimeException(
|
||||
gcName + " unexpectedly missing phase control support");
|
||||
}
|
||||
|
||||
// Verify IllegalArgumentException thrown by request attempt
|
||||
// with unknown phase.
|
||||
boolean illegalArgumentThrown = false;
|
||||
try {
|
||||
WB.requestConcurrentGCPhase("UNKNOWN PHASE");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
illegalArgumentThrown = true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
gcName + ": Unexpected exception when requesting unknown phase: " + e.toString());
|
||||
}
|
||||
if (!illegalArgumentThrown) {
|
||||
throw new RuntimeException(
|
||||
gcName + ": No exception when requesting unknown phase");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* Utility class that provides verification of expected behavior of
|
||||
* the Concurrent GC Phase Control WB API when the current GC does not
|
||||
* support phase control. The invoking test must provide WhiteBox access.
|
||||
*/
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
public class CheckUnsupported {
|
||||
|
||||
private static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
public static void check(String gcName) throws Exception {
|
||||
// Verify unsupported.
|
||||
if (WB.supportsConcurrentGCPhaseControl()) {
|
||||
throw new RuntimeException(
|
||||
gcName + " unexpectedly supports phase control");
|
||||
}
|
||||
|
||||
// Verify IllegalStateException thrown by request attempt.
|
||||
boolean illegalStateThrown = false;
|
||||
try {
|
||||
WB.requestConcurrentGCPhase("UNKNOWN PHASE");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
illegalStateThrown = true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
gcName + ": Unexpected exception when requesting phase: "
|
||||
+ e.toString());
|
||||
}
|
||||
if (!illegalStateThrown) {
|
||||
throw new RuntimeException(
|
||||
gcName + ": No exception when requesting phase");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* @test TestConcurrentPhaseControlG1
|
||||
* @bug 8169517
|
||||
* @requires vm.gc.G1
|
||||
* @summary Test of WhiteBox concurrent GC phase control for G1.
|
||||
* @key gc
|
||||
* @modules java.base
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run driver gc.concurrent_phase_control.TestConcurrentPhaseControlG1
|
||||
*/
|
||||
|
||||
import gc.concurrent_phase_control.CheckControl;
|
||||
|
||||
public class TestConcurrentPhaseControlG1 {
|
||||
|
||||
// Pairs of phase name and regex to match log stringm for stepping through,
|
||||
private static final String[][] g1PhaseInfo = {
|
||||
// Step through the phases in order.
|
||||
{"IDLE", null},
|
||||
{"CONCURRENT_CYCLE", "Concurrent Cycle"},
|
||||
{"IDLE", null}, // Resume IDLE before testing subphases
|
||||
{"CLEAR_CLAIMED_MARKS", "Concurrent Clear Claimed Marks"},
|
||||
{"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"},
|
||||
// ^F so not "From Roots", ^R so not "Restart"
|
||||
{"CONCURRENT_MARK", "Concurrent Mark [^FR]"},
|
||||
{"IDLE", null}, // Resume IDLE before testing subphases
|
||||
{"MARK_FROM_ROOTS", "Concurrent Mark From Roots"},
|
||||
{"PRECLEAN", "Concurrent Preclean"},
|
||||
{"BEFORE_REMARK", null},
|
||||
{"REMARK", "Pause Remark"},
|
||||
{"REBUILD_REMEMBERED_SETS", "Concurrent Rebuild Remembered Sets"},
|
||||
// Clear request
|
||||
{"IDLE", null},
|
||||
{"ANY", null},
|
||||
// Request a phase.
|
||||
{"MARK_FROM_ROOTS", "Concurrent Mark From Roots"},
|
||||
// Request an earlier phase, to ensure loop rather than stuck at idle.
|
||||
{"SCAN_ROOT_REGIONS", "Concurrent Scan Root Regions"},
|
||||
// Clear request, to unblock service.
|
||||
{"IDLE", null},
|
||||
{"ANY", null},
|
||||
};
|
||||
|
||||
private static final String[] g1Options =
|
||||
new String[]{"-XX:+UseG1GC", "-Xlog:gc,gc+marking"};
|
||||
|
||||
private static final String g1Name = "G1";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
CheckControl.check(g1Name, g1Options, g1PhaseInfo);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* @test TestConcurrentPhaseControlParallel
|
||||
* @bug 8169517
|
||||
* @requires vm.gc.Parallel
|
||||
* @summary Verify Parallel GC doesn't support WhiteBox concurrent phase control.
|
||||
* @key gc
|
||||
* @modules java.base
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -XX:+UseParallelGC
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* gc.concurrent_phase_control.TestConcurrentPhaseControlParallel
|
||||
*/
|
||||
|
||||
import gc.concurrent_phase_control.CheckUnsupported;
|
||||
|
||||
public class TestConcurrentPhaseControlParallel {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
CheckUnsupported.check("Parallel");
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, 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.
|
||||
*/
|
||||
|
||||
package gc.concurrent_phase_control;
|
||||
|
||||
/*
|
||||
* @test TestConcurrentPhaseControlSerial
|
||||
* @bug 8169517
|
||||
* @requires vm.gc.Serial
|
||||
* @summary Verify Serial GC doesn't support WhiteBox concurrent phase control.
|
||||
* @key gc
|
||||
* @modules java.base
|
||||
* @library /test/lib /
|
||||
* @build sun.hotspot.WhiteBox
|
||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* @run main/othervm -XX:+UseSerialGC
|
||||
* -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* gc.concurrent_phase_control.TestConcurrentPhaseControlSerial
|
||||
*/
|
||||
|
||||
import gc.concurrent_phase_control.CheckUnsupported;
|
||||
|
||||
public class TestConcurrentPhaseControlSerial {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
CheckUnsupported.check("Serial");
|
||||
}
|
||||
}
|
@ -411,33 +411,77 @@ public class WhiteBox {
|
||||
// Force Full GC
|
||||
public native void fullGC();
|
||||
|
||||
// Returns true if the current GC supports control of its concurrent
|
||||
// phase via requestConcurrentGCPhase(). If false, a request will
|
||||
// always fail.
|
||||
public native boolean supportsConcurrentGCPhaseControl();
|
||||
// Returns true if the current GC supports concurrent collection control.
|
||||
public native boolean supportsConcurrentGCBreakpoints();
|
||||
|
||||
// Attempt to put the collector into the indicated concurrent phase,
|
||||
// and attempt to remain in that state until a new request is made.
|
||||
//
|
||||
// Returns immediately if already in the requested phase.
|
||||
// Otherwise, waits until the phase is reached.
|
||||
//
|
||||
// Throws IllegalStateException if unsupported by the current collector.
|
||||
// Throws NullPointerException if phase is null.
|
||||
// Throws IllegalArgumentException if phase is not valid for the current collector.
|
||||
public void requestConcurrentGCPhase(String phase) {
|
||||
if (!supportsConcurrentGCPhaseControl()) {
|
||||
throw new IllegalStateException("Concurrent GC phase control not supported");
|
||||
} else if (phase == null) {
|
||||
throw new NullPointerException("null phase");
|
||||
} else if (!requestConcurrentGCPhase0(phase)) {
|
||||
throw new IllegalArgumentException("Unknown concurrent GC phase: " + phase);
|
||||
private void checkConcurrentGCBreakpointsSupported() {
|
||||
if (!supportsConcurrentGCBreakpoints()) {
|
||||
throw new UnsupportedOperationException("Concurrent GC breakpoints not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for requestConcurrentGCPhase(). Returns true if request
|
||||
// succeeded, false if the phase is invalid.
|
||||
private native boolean requestConcurrentGCPhase0(String phase);
|
||||
private native void concurrentGCAcquireControl0();
|
||||
private native void concurrentGCReleaseControl0();
|
||||
private native void concurrentGCRunToIdle0();
|
||||
private native boolean concurrentGCRunTo0(String breakpoint);
|
||||
|
||||
private static boolean concurrentGCIsControlled = false;
|
||||
private void checkConcurrentGCIsControlled() {
|
||||
if (!concurrentGCIsControlled) {
|
||||
throw new IllegalStateException("Not controlling concurrent GC");
|
||||
}
|
||||
}
|
||||
|
||||
// All collectors supporting concurrent GC breakpoints are expected
|
||||
// to provide at least the following breakpoints.
|
||||
public final String AFTER_MARKING_STARTED = "AFTER MARKING STARTED";
|
||||
public final String BEFORE_MARKING_COMPLETED = "BEFORE MARKING COMPLETED";
|
||||
|
||||
public void concurrentGCAcquireControl() {
|
||||
checkConcurrentGCBreakpointsSupported();
|
||||
if (concurrentGCIsControlled) {
|
||||
throw new IllegalStateException("Already controlling concurrent GC");
|
||||
}
|
||||
concurrentGCAcquireControl0();
|
||||
concurrentGCIsControlled = true;
|
||||
}
|
||||
|
||||
public void concurrentGCReleaseControl() {
|
||||
checkConcurrentGCBreakpointsSupported();
|
||||
concurrentGCReleaseControl0();
|
||||
concurrentGCIsControlled = false;
|
||||
}
|
||||
|
||||
// Keep concurrent GC idle. Release from breakpoint.
|
||||
public void concurrentGCRunToIdle() {
|
||||
checkConcurrentGCBreakpointsSupported();
|
||||
checkConcurrentGCIsControlled();
|
||||
concurrentGCRunToIdle0();
|
||||
}
|
||||
|
||||
// Allow concurrent GC to run to breakpoint.
|
||||
// Throws IllegalStateException if reached end of cycle first.
|
||||
public void concurrentGCRunTo(String breakpoint) {
|
||||
concurrentGCRunTo(breakpoint, true);
|
||||
}
|
||||
|
||||
// Allow concurrent GC to run to breakpoint.
|
||||
// Returns true if reached breakpoint. If reached end of cycle first,
|
||||
// then throws IllegalStateException if errorIfFail is true, returning
|
||||
// false otherwise.
|
||||
public boolean concurrentGCRunTo(String breakpoint, boolean errorIfFail) {
|
||||
checkConcurrentGCBreakpointsSupported();
|
||||
checkConcurrentGCIsControlled();
|
||||
if (breakpoint == null) {
|
||||
throw new NullPointerException("null breakpoint");
|
||||
} else if (concurrentGCRunTo0(breakpoint)) {
|
||||
return true;
|
||||
} else if (errorIfFail) {
|
||||
throw new IllegalStateException("Missed requested breakpoint \"" + breakpoint + "\"");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method tries to start concurrent mark cycle.
|
||||
// It returns false if CM Thread is always in concurrent cycle.
|
||||
|
Loading…
Reference in New Issue
Block a user