Skip to content

Commit 3a8a432

Browse files
author
William Kemper
committedMar 4, 2025
8349094: GenShen: Race between control and regulator threads may violate assertions
Reviewed-by: ysr, kdnilsen
1 parent 99fb350 commit 3a8a432

18 files changed

+669
-636
lines changed
 

‎src/hotspot/share/gc/shared/gcCause.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class GCCause : public AllStatic {
7474

7575
_shenandoah_stop_vm,
7676
_shenandoah_allocation_failure_evac,
77+
_shenandoah_humongous_allocation_failure,
7778
_shenandoah_concurrent_gc,
7879
_shenandoah_upgrade_to_full_gc,
7980

‎src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp

+30-12
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ int ShenandoahOldHeuristics::compare_by_index(RegionData a, RegionData b) {
6060
ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap) :
6161
ShenandoahHeuristics(generation),
6262
_heap(gen_heap),
63-
_old_gen(generation),
6463
_first_pinned_candidate(NOT_FOUND),
6564
_last_old_collection_candidate(0),
6665
_next_old_collection_candidate(0),
@@ -567,9 +566,9 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
567566
// allocation request will require a STW full GC.
568567
size_t allowed_old_gen_span = num_regions - (ShenandoahGenerationalHumongousReserve * num_regions) / 100;
569568

570-
size_t old_available = _old_gen->available() / HeapWordSize;
569+
size_t old_available = _old_generation->available() / HeapWordSize;
571570
size_t region_size_words = ShenandoahHeapRegion::region_size_words();
572-
size_t old_unaffiliated_available = _old_gen->free_unaffiliated_regions() * region_size_words;
571+
size_t old_unaffiliated_available = _old_generation->free_unaffiliated_regions() * region_size_words;
573572
assert(old_available >= old_unaffiliated_available, "sanity");
574573
size_t old_fragmented_available = old_available - old_unaffiliated_available;
575574

@@ -603,12 +602,12 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
603602
}
604603

605604
void ShenandoahOldHeuristics::set_trigger_if_old_is_overgrown() {
606-
size_t old_used = _old_gen->used() + _old_gen->get_humongous_waste();
607-
size_t trigger_threshold = _old_gen->usage_trigger_threshold();
605+
size_t old_used = _old_generation->used() + _old_generation->get_humongous_waste();
606+
size_t trigger_threshold = _old_generation->usage_trigger_threshold();
608607
// Detects unsigned arithmetic underflow
609608
assert(old_used <= _heap->capacity(),
610609
"Old used (%zu, %zu) must not be more than heap capacity (%zu)",
611-
_old_gen->used(), _old_gen->get_humongous_waste(), _heap->capacity());
610+
_old_generation->used(), _old_generation->get_humongous_waste(), _heap->capacity());
612611
if (old_used > trigger_threshold) {
613612
_growth_trigger = true;
614613
}
@@ -620,13 +619,32 @@ void ShenandoahOldHeuristics::evaluate_triggers(size_t first_old_region, size_t
620619
set_trigger_if_old_is_overgrown();
621620
}
622621

622+
bool ShenandoahOldHeuristics::should_resume_old_cycle() {
623+
// If we are preparing to mark old, or if we are already marking old, then try to continue that work.
624+
if (_old_generation->is_concurrent_mark_in_progress()) {
625+
assert(_old_generation->state() == ShenandoahOldGeneration::MARKING, "Unexpected old gen state: %s", _old_generation->state_name());
626+
log_trigger("Resume marking old");
627+
return true;
628+
}
629+
630+
if (_old_generation->is_preparing_for_mark()) {
631+
assert(_old_generation->state() == ShenandoahOldGeneration::FILLING, "Unexpected old gen state: %s", _old_generation->state_name());
632+
log_trigger("Resume preparing to mark old");
633+
return true;
634+
}
635+
636+
return false;
637+
}
638+
623639
bool ShenandoahOldHeuristics::should_start_gc() {
624-
// Cannot start a new old-gen GC until previous one has finished.
625-
//
626-
// Future refinement: under certain circumstances, we might be more sophisticated about this choice.
627-
// For example, we could choose to abandon the previous old collection before it has completed evacuations.
628-
ShenandoahHeap* heap = ShenandoahHeap::heap();
629-
if (!_old_generation->can_start_gc() || heap->collection_set()->has_old_regions()) {
640+
641+
const ShenandoahHeap* heap = ShenandoahHeap::heap();
642+
if (_old_generation->is_doing_mixed_evacuations()) {
643+
// Do not try to start an old cycle if we are waiting for old regions to be evacuated (we need
644+
// a young cycle for this). Note that the young heuristic has a feature to expedite old evacuations.
645+
// Future refinement: under certain circumstances, we might be more sophisticated about this choice.
646+
// For example, we could choose to abandon the previous old collection before it has completed evacuations.
647+
log_debug(gc)("Not starting an old cycle because we are waiting for mixed evacuations");
630648
return false;
631649
}
632650

‎src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ class ShenandoahOldHeuristics : public ShenandoahHeuristics {
5353
static uint NOT_FOUND;
5454

5555
ShenandoahGenerationalHeap* _heap;
56-
ShenandoahOldGeneration* _old_gen;
5756

5857
// After final marking of the old generation, this heuristic will select
5958
// a set of candidate regions to be included in subsequent mixed collections.
@@ -186,6 +185,9 @@ class ShenandoahOldHeuristics : public ShenandoahHeuristics {
186185

187186
bool should_start_gc() override;
188187

188+
// Returns true if the old generation needs to prepare for marking, or continue marking.
189+
bool should_resume_old_cycle();
190+
189191
void record_success_concurrent() override;
190192

191193
void record_success_degenerated() override;

‎src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, Basi
9090
void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) {
9191
#if COMPILER2_OR_JVMCI
9292
assert(!ReduceInitialCardMarks || !ShenandoahCardBarrier || ShenandoahGenerationalHeap::heap()->is_in_young(new_obj),
93-
"Error: losing card mark on initialzing store to old gen");
93+
"Allocating new object outside of young generation: " INTPTR_FORMAT, p2i(new_obj));
9494
#endif // COMPILER2_OR_JVMCI
9595
assert(thread->deferred_card_mark().is_empty(), "We don't use this");
9696
}

‎src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp

+24-5
Original file line numberDiff line numberDiff line change
@@ -123,25 +123,28 @@ void ShenandoahCollectorPolicy::record_shutdown() {
123123
_in_shutdown.set();
124124
}
125125

126-
bool ShenandoahCollectorPolicy::is_at_shutdown() {
126+
bool ShenandoahCollectorPolicy::is_at_shutdown() const {
127127
return _in_shutdown.is_set();
128128
}
129129

130-
bool is_explicit_gc(GCCause::Cause cause) {
130+
bool ShenandoahCollectorPolicy::is_explicit_gc(GCCause::Cause cause) {
131131
return GCCause::is_user_requested_gc(cause)
132-
|| GCCause::is_serviceability_requested_gc(cause);
132+
|| GCCause::is_serviceability_requested_gc(cause)
133+
|| cause == GCCause::_wb_full_gc
134+
|| cause == GCCause::_wb_young_gc;
133135
}
134136

135137
bool is_implicit_gc(GCCause::Cause cause) {
136138
return cause != GCCause::_no_gc
137139
&& cause != GCCause::_shenandoah_concurrent_gc
138140
&& cause != GCCause::_allocation_failure
139-
&& !is_explicit_gc(cause);
141+
&& !ShenandoahCollectorPolicy::is_explicit_gc(cause);
140142
}
141143

142144
#ifdef ASSERT
143145
bool is_valid_request(GCCause::Cause cause) {
144-
return is_explicit_gc(cause)
146+
return ShenandoahCollectorPolicy::is_explicit_gc(cause)
147+
|| ShenandoahCollectorPolicy::is_shenandoah_gc(cause)
145148
|| cause == GCCause::_metadata_GC_clear_soft_refs
146149
|| cause == GCCause::_codecache_GC_aggressive
147150
|| cause == GCCause::_codecache_GC_threshold
@@ -153,6 +156,22 @@ bool is_valid_request(GCCause::Cause cause) {
153156
}
154157
#endif
155158

159+
bool ShenandoahCollectorPolicy::is_shenandoah_gc(GCCause::Cause cause) {
160+
return cause == GCCause::_allocation_failure
161+
|| cause == GCCause::_shenandoah_stop_vm
162+
|| cause == GCCause::_shenandoah_allocation_failure_evac
163+
|| cause == GCCause::_shenandoah_humongous_allocation_failure
164+
|| cause == GCCause::_shenandoah_concurrent_gc
165+
|| cause == GCCause::_shenandoah_upgrade_to_full_gc;
166+
}
167+
168+
169+
bool ShenandoahCollectorPolicy::is_allocation_failure(GCCause::Cause cause) {
170+
return cause == GCCause::_allocation_failure
171+
|| cause == GCCause::_shenandoah_allocation_failure_evac
172+
|| cause == GCCause::_shenandoah_humongous_allocation_failure;
173+
}
174+
156175
bool ShenandoahCollectorPolicy::is_requested_gc(GCCause::Cause cause) {
157176
return is_explicit_gc(cause) || is_implicit_gc(cause);
158177
}

‎src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
7777
void record_collection_cause(GCCause::Cause cause);
7878

7979
void record_shutdown();
80-
bool is_at_shutdown();
80+
bool is_at_shutdown() const;
8181

82-
ShenandoahTracer* tracer() {return _tracer;}
82+
ShenandoahTracer* tracer() const {return _tracer;}
8383

8484
void print_gc_stats(outputStream* out) const;
8585

@@ -90,15 +90,18 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
9090
// If the heuristics find that the number of consecutive degenerated cycles is above
9191
// ShenandoahFullGCThreshold, then they will initiate a Full GC upon an allocation
9292
// failure.
93-
inline size_t consecutive_degenerated_gc_count() const {
93+
size_t consecutive_degenerated_gc_count() const {
9494
return _consecutive_degenerated_gcs;
9595
}
9696

97+
static bool is_allocation_failure(GCCause::Cause cause);
98+
static bool is_shenandoah_gc(GCCause::Cause cause);
9799
static bool is_requested_gc(GCCause::Cause cause);
100+
static bool is_explicit_gc(GCCause::Cause cause);
98101
static bool should_run_full_gc(GCCause::Cause cause);
99102
static bool should_handle_requested_gc(GCCause::Cause cause);
100103

101-
inline size_t consecutive_young_gc_count() const {
104+
size_t consecutive_young_gc_count() const {
102105
return _consecutive_young_gcs;
103106
}
104107

‎src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp

+22-13
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ ShenandoahControlThread::ShenandoahControlThread() :
5050

5151
void ShenandoahControlThread::run_service() {
5252
ShenandoahHeap* const heap = ShenandoahHeap::heap();
53-
5453
const GCMode default_mode = concurrent_normal;
5554
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
5655
int sleep = ShenandoahControlIntervalMin;
@@ -59,9 +58,14 @@ void ShenandoahControlThread::run_service() {
5958

6059
ShenandoahCollectorPolicy* const policy = heap->shenandoah_policy();
6160
ShenandoahHeuristics* const heuristics = heap->heuristics();
62-
while (!in_graceful_shutdown() && !should_terminate()) {
61+
while (!should_terminate()) {
62+
const GCCause::Cause cancelled_cause = heap->cancelled_cause();
63+
if (cancelled_cause == GCCause::_shenandoah_stop_vm) {
64+
break;
65+
}
66+
6367
// Figure out if we have pending requests.
64-
const bool alloc_failure_pending = _alloc_failure_gc.is_set();
68+
const bool alloc_failure_pending = ShenandoahCollectorPolicy::is_allocation_failure(cancelled_cause);
6569
const bool is_gc_requested = _gc_requested.is_set();
6670
const GCCause::Cause requested_gc_cause = _requested_gc_cause;
6771

@@ -254,11 +258,6 @@ void ShenandoahControlThread::run_service() {
254258
}
255259
os::naked_short_sleep(sleep);
256260
}
257-
258-
// Wait for the actual stop(), can't leave run_service() earlier.
259-
while (!should_terminate()) {
260-
os::naked_short_sleep(ShenandoahControlIntervalMin);
261-
}
262261
}
263262

264263
void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cause) {
@@ -322,19 +321,24 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau
322321
bool ShenandoahControlThread::check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point) {
323322
ShenandoahHeap* heap = ShenandoahHeap::heap();
324323
if (heap->cancelled_gc()) {
325-
assert (is_alloc_failure_gc() || in_graceful_shutdown(), "Cancel GC either for alloc failure GC, or gracefully exiting");
326-
if (!in_graceful_shutdown()) {
324+
if (heap->cancelled_cause() == GCCause::_shenandoah_stop_vm) {
325+
return true;
326+
}
327+
328+
if (ShenandoahCollectorPolicy::is_allocation_failure(heap->cancelled_cause())) {
327329
assert (_degen_point == ShenandoahGC::_degenerated_outside_cycle,
328330
"Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point));
329331
_degen_point = point;
332+
return true;
330333
}
331-
return true;
334+
335+
fatal("Unexpected reason for cancellation: %s", GCCause::to_string(heap->cancelled_cause()));
332336
}
333337
return false;
334338
}
335339

336340
void ShenandoahControlThread::stop_service() {
337-
// Nothing to do here.
341+
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_stop_vm);
338342
}
339343

340344
void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) {
@@ -363,6 +367,11 @@ void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
363367
}
364368

365369
void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
370+
if (should_terminate()) {
371+
log_info(gc)("Control thread is terminating, no more GCs");
372+
return;
373+
}
374+
366375
// For normal requested GCs (System.gc) we want to block the caller. However,
367376
// for whitebox requested GC, we want to initiate the GC and return immediately.
368377
// The whitebox caller thread will arrange for itself to wait until the GC notifies
@@ -385,7 +394,7 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
385394
MonitorLocker ml(&_gc_waiters_lock);
386395
size_t current_gc_id = get_gc_id();
387396
size_t required_gc_id = current_gc_id + 1;
388-
while (current_gc_id < required_gc_id) {
397+
while (current_gc_id < required_gc_id && !should_terminate()) {
389398
// Although setting gc request is under _gc_waiters_lock, but read side (run_service())
390399
// does not take the lock. We need to enforce following order, so that read side sees
391400
// latest requested gc cause when the flag is set.

‎src/hotspot/share/gc/shenandoah/shenandoahController.cpp

+16-43
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include "gc/shared/gc_globals.hpp"
2727
#include "gc/shenandoah/shenandoahController.hpp"
28+
29+
#include "shenandoahCollectorPolicy.hpp"
2830
#include "gc/shenandoah/shenandoahHeap.hpp"
2931
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
3032

@@ -37,14 +39,6 @@ size_t ShenandoahController::reset_allocs_seen() {
3739
return Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed);
3840
}
3941

40-
void ShenandoahController::prepare_for_graceful_shutdown() {
41-
_graceful_shutdown.set();
42-
}
43-
44-
bool ShenandoahController::in_graceful_shutdown() {
45-
return _graceful_shutdown.is_set();
46-
}
47-
4842
void ShenandoahController::update_gc_id() {
4943
Atomic::inc(&_gc_id);
5044
}
@@ -53,59 +47,38 @@ size_t ShenandoahController::get_gc_id() {
5347
return Atomic::load(&_gc_id);
5448
}
5549

56-
void ShenandoahController::handle_alloc_failure(ShenandoahAllocRequest& req, bool block) {
57-
ShenandoahHeap* heap = ShenandoahHeap::heap();
58-
50+
void ShenandoahController::handle_alloc_failure(const ShenandoahAllocRequest& req, bool block) {
5951
assert(current()->is_Java_thread(), "expect Java thread here");
60-
bool is_humongous = ShenandoahHeapRegion::requires_humongous(req.size());
6152

62-
if (try_set_alloc_failure_gc(is_humongous)) {
63-
// Only report the first allocation failure
64-
log_info(gc)("Failed to allocate %s, %zu%s",
65-
req.type_string(),
66-
byte_size_in_proper_unit(req.size() * HeapWordSize), proper_unit_for_byte_size(req.size() * HeapWordSize));
53+
const bool is_humongous = ShenandoahHeapRegion::requires_humongous(req.size());
54+
const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_allocation_failure;
6755

68-
// Now that alloc failure GC is scheduled, we can abort everything else
69-
heap->cancel_gc(GCCause::_allocation_failure);
56+
ShenandoahHeap* const heap = ShenandoahHeap::heap();
57+
if (heap->cancel_gc(cause)) {
58+
log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req.size() * HeapWordSize));
59+
request_gc(cause);
7060
}
7161

72-
7362
if (block) {
7463
MonitorLocker ml(&_alloc_failure_waiters_lock);
75-
while (is_alloc_failure_gc()) {
64+
while (!should_terminate() && ShenandoahCollectorPolicy::is_allocation_failure(heap->cancelled_cause())) {
7665
ml.wait();
7766
}
7867
}
7968
}
8069

8170
void ShenandoahController::handle_alloc_failure_evac(size_t words) {
82-
ShenandoahHeap* heap = ShenandoahHeap::heap();
83-
bool is_humongous = ShenandoahHeapRegion::requires_humongous(words);
8471

85-
if (try_set_alloc_failure_gc(is_humongous)) {
86-
// Only report the first allocation failure
87-
log_info(gc)("Failed to allocate %zu%s for evacuation",
88-
byte_size_in_proper_unit(words * HeapWordSize), proper_unit_for_byte_size(words * HeapWordSize));
89-
}
72+
ShenandoahHeap* const heap = ShenandoahHeap::heap();
73+
const bool is_humongous = ShenandoahHeapRegion::requires_humongous(words);
74+
const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_shenandoah_allocation_failure_evac;
9075

91-
// Forcefully report allocation failure
92-
heap->cancel_gc(GCCause::_shenandoah_allocation_failure_evac);
76+
if (heap->cancel_gc(cause)) {
77+
log_info(gc)("Failed to allocate " PROPERFMT " for evacuation", PROPERFMTARGS(words * HeapWordSize));
78+
}
9379
}
9480

9581
void ShenandoahController::notify_alloc_failure_waiters() {
96-
_alloc_failure_gc.unset();
97-
_humongous_alloc_failure_gc.unset();
9882
MonitorLocker ml(&_alloc_failure_waiters_lock);
9983
ml.notify_all();
10084
}
101-
102-
bool ShenandoahController::try_set_alloc_failure_gc(bool is_humongous) {
103-
if (is_humongous) {
104-
_humongous_alloc_failure_gc.try_set();
105-
}
106-
return _alloc_failure_gc.try_set();
107-
}
108-
109-
bool ShenandoahController::is_alloc_failure_gc() {
110-
return _alloc_failure_gc.is_set();
111-
}

‎src/hotspot/share/gc/shenandoah/shenandoahController.hpp

+9-24
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,25 @@
3636
*/
3737
class ShenandoahController: public ConcurrentGCThread {
3838
private:
39-
ShenandoahSharedFlag _graceful_shutdown;
40-
4139
shenandoah_padding(0);
4240
volatile size_t _allocs_seen;
4341
shenandoah_padding(1);
42+
// A monotonically increasing GC count.
4443
volatile size_t _gc_id;
4544
shenandoah_padding(2);
4645

4746
protected:
48-
ShenandoahSharedFlag _alloc_failure_gc;
49-
ShenandoahSharedFlag _humongous_alloc_failure_gc;
50-
5147
// While we could have a single lock for these, it may risk unblocking
5248
// GC waiters when alloc failure GC cycle finishes. We want instead
5349
// to make complete explicit cycle for demanding customers.
5450
Monitor _alloc_failure_waiters_lock;
5551
Monitor _gc_waiters_lock;
5652

53+
// Increments the internal GC count.
54+
void update_gc_id();
55+
5756
public:
5857
ShenandoahController():
59-
ConcurrentGCThread(),
6058
_allocs_seen(0),
6159
_gc_id(0),
6260
_alloc_failure_waiters_lock(Mutex::safepoint-2, "ShenandoahAllocFailureGC_lock", true),
@@ -68,38 +66,25 @@ class ShenandoahController: public ConcurrentGCThread {
6866
virtual void request_gc(GCCause::Cause cause) = 0;
6967

7068
// This cancels the collection cycle and has an option to block
71-
// until another cycle runs and clears the alloc failure gc flag.
72-
void handle_alloc_failure(ShenandoahAllocRequest& req, bool block);
69+
// until another cycle completes successfully.
70+
void handle_alloc_failure(const ShenandoahAllocRequest& req, bool block);
7371

7472
// Invoked for allocation failures during evacuation. This cancels
7573
// the collection cycle without blocking.
7674
void handle_alloc_failure_evac(size_t words);
7775

78-
// Return true if setting the flag which indicates allocation failure succeeds.
79-
bool try_set_alloc_failure_gc(bool is_humongous);
80-
8176
// Notify threads waiting for GC to complete.
8277
void notify_alloc_failure_waiters();
8378

84-
// True if allocation failure flag has been set.
85-
bool is_alloc_failure_gc();
86-
8779
// This is called for every allocation. The control thread accumulates
8880
// this value when idle. During the gc cycle, the control resets it
8981
// and reports it to the pacer.
9082
void pacing_notify_alloc(size_t words);
91-
size_t reset_allocs_seen();
92-
93-
// These essentially allows to cancel a collection cycle for the
94-
// purpose of shutting down the JVM, without trying to start a degenerated
95-
// cycle.
96-
void prepare_for_graceful_shutdown();
97-
bool in_graceful_shutdown();
9883

84+
// Zeros out the number of allocations seen since the last GC cycle.
85+
size_t reset_allocs_seen();
9986

100-
// Returns the internal gc count used by the control thread. Probably
101-
// doesn't need to be exposed.
87+
// Return the value of a monotonic increasing GC count, maintained by the control thread.
10288
size_t get_gc_id();
103-
void update_gc_id();
10489
};
10590
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP

‎src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp

+411-441
Large diffs are not rendered by default.

‎src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp

+70-35
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@
2828

2929
#include "gc/shared/gcCause.hpp"
3030
#include "gc/shenandoah/shenandoahController.hpp"
31-
#include "gc/shenandoah/shenandoahGenerationType.hpp"
3231
#include "gc/shenandoah/shenandoahGC.hpp"
33-
#include "gc/shenandoah/shenandoahPadding.hpp"
3432
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
33+
#include "runtime/mutexLocker.hpp"
3534

3635
class ShenandoahOldGeneration;
3736
class ShenandoahGeneration;
@@ -52,21 +51,43 @@ class ShenandoahGenerationalControlThread: public ShenandoahController {
5251
stopped
5352
} GCMode;
5453

54+
class ShenandoahGCRequest {
55+
public:
56+
ShenandoahGCRequest() : generation(nullptr), cause(GCCause::_no_gc) {}
57+
ShenandoahGeneration* generation;
58+
GCCause::Cause cause;
59+
};
60+
5561
private:
62+
// This lock is used to coordinate setting the _requested_gc_cause, _requested generation
63+
// and _gc_mode. It is important that these be changed together and have a consistent view.
5664
Monitor _control_lock;
57-
Monitor _regulator_lock;
58-
59-
ShenandoahSharedFlag _allow_old_preemption;
60-
ShenandoahSharedFlag _preemption_requested;
6165

66+
// Represents a normal (non cancellation) gc request. This can be set by mutators (System.gc,
67+
// whitebox gc, etc.) or by the regulator thread when the heuristics want to start a cycle.
6268
GCCause::Cause _requested_gc_cause;
63-
volatile ShenandoahGenerationType _requested_generation;
69+
70+
// This is the generation the request should operate on.
71+
ShenandoahGeneration* _requested_generation;
72+
73+
// The mode is read frequently by requesting threads and only ever written by the control thread.
74+
// This may be read without taking the _control_lock, but should be read again under the lock
75+
// before making any state changes (double-checked locking idiom).
76+
volatile GCMode _gc_mode;
77+
78+
// Only the control thread knows the correct degeneration point. This is used to have the
79+
// control thread resume a STW cycle from the point where the concurrent cycle was cancelled.
6480
ShenandoahGC::ShenandoahDegenPoint _degen_point;
65-
ShenandoahGeneration* _degen_generation;
6681

67-
shenandoah_padding(0);
68-
volatile GCMode _mode;
69-
shenandoah_padding(1);
82+
// A reference to the heap
83+
ShenandoahGenerationalHeap* _heap;
84+
85+
// This is used to keep track of whether to age objects during the current cycle.
86+
uint _age_period;
87+
88+
// This is true when the old generation cycle is in an interruptible phase (i.e., marking or
89+
// preparing for mark).
90+
ShenandoahSharedFlag _allow_old_preemption;
7091

7192
public:
7293
ShenandoahGenerationalControlThread();
@@ -77,54 +98,68 @@ class ShenandoahGenerationalControlThread: public ShenandoahController {
7798
void request_gc(GCCause::Cause cause) override;
7899

79100
// Return true if the request to start a concurrent GC for the given generation succeeded.
80-
bool request_concurrent_gc(ShenandoahGenerationType generation);
101+
bool request_concurrent_gc(ShenandoahGeneration* generation);
81102

82-
GCMode gc_mode() {
83-
return _mode;
103+
// Returns the current state of the control thread
104+
GCMode gc_mode() const {
105+
return _gc_mode;
84106
}
85107
private:
86-
87108
// Returns true if the cycle has been cancelled or degenerated.
88109
bool check_cancellation_or_degen(ShenandoahGC::ShenandoahDegenPoint point);
89110

111+
// Executes one GC cycle
112+
void run_gc_cycle(const ShenandoahGCRequest& request);
113+
90114
// Returns true if the old generation marking completed (i.e., final mark executed for old generation).
91115
bool resume_concurrent_old_cycle(ShenandoahOldGeneration* generation, GCCause::Cause cause);
116+
117+
// Various service methods handle different gc cycle types
92118
void service_concurrent_cycle(ShenandoahGeneration* generation, GCCause::Cause cause, bool reset_old_bitmap_specially);
93119
void service_stw_full_cycle(GCCause::Cause cause);
94-
void service_stw_degenerated_cycle(GCCause::Cause cause, ShenandoahGC::ShenandoahDegenPoint point);
120+
void service_stw_degenerated_cycle(const ShenandoahGCRequest& request);
121+
void service_concurrent_normal_cycle(const ShenandoahGCRequest& request);
122+
void service_concurrent_old_cycle(const ShenandoahGCRequest& request);
95123

96124
void notify_gc_waiters();
97125

98-
// Handle GC request.
99-
// Blocks until GC is over.
126+
// Blocks until at least one global GC cycle is complete.
100127
void handle_requested_gc(GCCause::Cause cause);
101128

102-
bool is_explicit_gc(GCCause::Cause cause) const;
103-
bool is_implicit_gc(GCCause::Cause cause) const;
104-
105129
// Returns true if the old generation marking was interrupted to allow a young cycle.
106-
bool preempt_old_marking(ShenandoahGenerationType generation);
130+
bool preempt_old_marking(ShenandoahGeneration* generation);
107131

108-
void process_phase_timings(const ShenandoahGenerationalHeap* heap);
109-
110-
void service_concurrent_normal_cycle(ShenandoahGenerationalHeap* heap,
111-
ShenandoahGenerationType generation,
112-
GCCause::Cause cause);
113-
114-
void service_concurrent_old_cycle(ShenandoahGenerationalHeap* heap,
115-
GCCause::Cause &cause);
132+
// Flushes cycle timings to global timings and prints the phase timings for the last completed cycle.
133+
void process_phase_timings() const;
116134

135+
// Set the gc mode and post a notification if it has changed. The overloaded variant should be used
136+
// when the _control_lock is already held.
117137
void set_gc_mode(GCMode new_mode);
138+
void set_gc_mode(MonitorLocker& ml, GCMode new_mode);
118139

140+
// Return printable name for the given gc mode.
119141
static const char* gc_mode_name(GCMode mode);
120142

121-
void notify_control_thread();
143+
// Takes the request lock and updates the requested cause and generation, then notifies the control thread.
144+
// The overloaded variant should be used when the _control_lock is already held.
145+
void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation);
146+
void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation);
147+
148+
// Notifies the control thread, but does not update the requested cause or generation.
149+
// The overloaded variant should be used when the _control_lock is already held.
150+
void notify_cancellation(GCCause::Cause cause);
151+
void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause);
152+
153+
// Configure the heap to age objects and regions if the aging period has elapsed.
154+
void maybe_set_aging_cycle();
122155

123-
void service_concurrent_cycle(ShenandoahHeap* heap,
124-
ShenandoahGeneration* generation,
125-
GCCause::Cause &cause,
126-
bool do_old_gc_bootstrap);
156+
// Take the _control_lock and check for a request to run a gc cycle. If a request is found,
157+
// the `prepare` methods are used to configure the heap and update heuristics accordingly.
158+
void check_for_request(ShenandoahGCRequest& request);
127159

160+
GCMode prepare_for_allocation_failure_gc(ShenandoahGCRequest &request);
161+
GCMode prepare_for_explicit_gc(ShenandoahGCRequest &request) const;
162+
GCMode prepare_for_concurrent_gc(const ShenandoahGCRequest &request) const;
128163
};
129164

130165
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP

‎src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp

+19-19
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) :
583583
{
584584
// Initialize GC mode early, many subsequent initialization procedures depend on it
585585
initialize_mode();
586+
_cancelled_gc.set(GCCause::_no_gc);
586587
}
587588

588589
#ifdef _MSC_VER
@@ -993,13 +994,13 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) {
993994
// a) We experienced a GC that had good progress, or
994995
// b) We experienced at least one Full GC (whether or not it had good progress)
995996

996-
size_t original_count = shenandoah_policy()->full_gc_count();
997-
while ((result == nullptr) && (original_count == shenandoah_policy()->full_gc_count())) {
997+
const size_t original_count = shenandoah_policy()->full_gc_count();
998+
while (result == nullptr && should_retry_allocation(original_count)) {
998999
control_thread()->handle_alloc_failure(req, true);
9991000
result = allocate_memory_under_lock(req, in_new_region);
10001001
}
10011002
if (result != nullptr) {
1002-
// If our allocation request has been satisifed after it initially failed, we count this as good gc progress
1003+
// If our allocation request has been satisfied after it initially failed, we count this as good gc progress
10031004
notify_gc_progress();
10041005
}
10051006
if (log_develop_is_enabled(Debug, gc, alloc)) {
@@ -1050,6 +1051,11 @@ HeapWord* ShenandoahHeap::allocate_memory(ShenandoahAllocRequest& req) {
10501051
return result;
10511052
}
10521053

1054+
inline bool ShenandoahHeap::should_retry_allocation(size_t original_full_gc_count) const {
1055+
return shenandoah_policy()->full_gc_count() == original_full_gc_count
1056+
&& !shenandoah_policy()->is_at_shutdown();
1057+
}
1058+
10531059
HeapWord* ShenandoahHeap::allocate_memory_under_lock(ShenandoahAllocRequest& req, bool& in_new_region) {
10541060
// If we are dealing with mutator allocation, then we may need to block for safepoint.
10551061
// We cannot block for safepoint for GC allocations, because there is a high chance
@@ -2120,9 +2126,9 @@ size_t ShenandoahHeap::tlab_used(Thread* thread) const {
21202126
return _free_set->used();
21212127
}
21222128

2123-
bool ShenandoahHeap::try_cancel_gc() {
2124-
jbyte prev = _cancelled_gc.cmpxchg(CANCELLED, CANCELLABLE);
2125-
return prev == CANCELLABLE;
2129+
bool ShenandoahHeap::try_cancel_gc(GCCause::Cause cause) {
2130+
jbyte prev = _cancelled_gc.cmpxchg(cause, GCCause::_no_gc);
2131+
return prev == GCCause::_no_gc;
21262132
}
21272133

21282134
void ShenandoahHeap::cancel_concurrent_mark() {
@@ -2136,13 +2142,15 @@ void ShenandoahHeap::cancel_concurrent_mark() {
21362142
ShenandoahBarrierSet::satb_mark_queue_set().abandon_partial_marking();
21372143
}
21382144

2139-
void ShenandoahHeap::cancel_gc(GCCause::Cause cause) {
2140-
if (try_cancel_gc()) {
2145+
bool ShenandoahHeap::cancel_gc(GCCause::Cause cause) {
2146+
if (try_cancel_gc(cause)) {
21412147
FormatBuffer<> msg("Cancelling GC: %s", GCCause::to_string(cause));
2142-
log_info(gc)("%s", msg.buffer());
2148+
log_info(gc,thread)("%s", msg.buffer());
21432149
Events::log(Thread::current(), "%s", msg.buffer());
21442150
_cancel_requested_time = os::elapsedTime();
2151+
return true;
21452152
}
2153+
return false;
21462154
}
21472155

21482156
uint ShenandoahHeap::max_workers() {
@@ -2155,18 +2163,10 @@ void ShenandoahHeap::stop() {
21552163
// Step 0. Notify policy to disable event recording and prevent visiting gc threads during shutdown
21562164
_shenandoah_policy->record_shutdown();
21572165

2158-
// Step 0a. Stop reporting on gc thread cpu utilization
2166+
// Step 1. Stop reporting on gc thread cpu utilization
21592167
mmu_tracker()->stop();
21602168

2161-
// Step 1. Notify control thread that we are in shutdown.
2162-
// Note that we cannot do that with stop(), because stop() is blocking and waits for the actual shutdown.
2163-
// Doing stop() here would wait for the normal GC cycle to complete, never falling through to cancel below.
2164-
control_thread()->prepare_for_graceful_shutdown();
2165-
2166-
// Step 2. Notify GC workers that we are cancelling GC.
2167-
cancel_gc(GCCause::_shenandoah_stop_vm);
2168-
2169-
// Step 3. Wait until GC worker exits normally.
2169+
// Step 2. Wait until GC worker exits normally (this will cancel any ongoing GC).
21702170
control_thread()->stop();
21712171

21722172
// Stop 4. Shutdown uncommit thread.

‎src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp

+20-14
Original file line numberDiff line numberDiff line change
@@ -430,35 +430,38 @@ class ShenandoahHeap : public CollectedHeap {
430430
private:
431431
void manage_satb_barrier(bool active);
432432

433-
enum CancelState {
434-
// Normal state. GC has not been cancelled and is open for cancellation.
435-
// Worker threads can suspend for safepoint.
436-
CANCELLABLE,
437-
438-
// GC has been cancelled. Worker threads can not suspend for
439-
// safepoint but must finish their work as soon as possible.
440-
CANCELLED
441-
};
442-
433+
// Records the time of the first successful cancellation request. This is used to measure
434+
// the responsiveness of the heuristic when starting a cycle.
443435
double _cancel_requested_time;
444-
ShenandoahSharedEnumFlag<CancelState> _cancelled_gc;
436+
437+
// Indicates the reason the current GC has been cancelled (GCCause::_no_gc means the gc is not cancelled).
438+
ShenandoahSharedEnumFlag<GCCause::Cause> _cancelled_gc;
445439

446440
// Returns true if cancel request was successfully communicated.
447441
// Returns false if some other thread already communicated cancel
448442
// request. A true return value does not mean GC has been
449443
// cancelled, only that the process of cancelling GC has begun.
450-
bool try_cancel_gc();
444+
bool try_cancel_gc(GCCause::Cause cause);
451445

452446
public:
447+
// True if gc has been cancelled
453448
inline bool cancelled_gc() const;
449+
450+
// Used by workers in the GC cycle to detect cancellation and honor STS requirements
454451
inline bool check_cancelled_gc_and_yield(bool sts_active = true);
455452

453+
// This indicates the reason the last GC cycle was cancelled.
454+
inline GCCause::Cause cancelled_cause() const;
455+
456+
// Clears the cancellation cause and optionally resets the oom handler (cancelling an
457+
// old mark does _not_ touch the oom handler).
456458
inline void clear_cancelled_gc(bool clear_oom_handler = true);
457459

458460
void cancel_concurrent_mark();
459-
void cancel_gc(GCCause::Cause cause);
460461

461-
public:
462+
// Returns true if and only if this call caused a gc to be cancelled.
463+
bool cancel_gc(GCCause::Cause cause);
464+
462465
// Returns true if the soft maximum heap has been changed using management APIs.
463466
bool check_soft_max_changed();
464467

@@ -690,6 +693,9 @@ class ShenandoahHeap : public CollectedHeap {
690693
HeapWord* allocate_from_gclab_slow(Thread* thread, size_t size);
691694
HeapWord* allocate_new_gclab(size_t min_size, size_t word_size, size_t* actual_size);
692695

696+
// We want to retry an unsuccessful attempt at allocation until at least a full gc.
697+
bool should_retry_allocation(size_t original_full_gc_count) const;
698+
693699
public:
694700
HeapWord* allocate_memory(ShenandoahAllocRequest& request);
695701
HeapWord* mem_allocate(size_t size, bool* what) override;

‎src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, narrowOop compare)
252252
}
253253

254254
inline bool ShenandoahHeap::cancelled_gc() const {
255-
return _cancelled_gc.get() == CANCELLED;
255+
return _cancelled_gc.get() != GCCause::_no_gc;
256256
}
257257

258258
inline bool ShenandoahHeap::check_cancelled_gc_and_yield(bool sts_active) {
@@ -264,8 +264,12 @@ inline bool ShenandoahHeap::check_cancelled_gc_and_yield(bool sts_active) {
264264
return cancelled_gc();
265265
}
266266

267+
inline GCCause::Cause ShenandoahHeap::cancelled_cause() const {
268+
return _cancelled_gc.get();
269+
}
270+
267271
inline void ShenandoahHeap::clear_cancelled_gc(bool clear_oom_handler) {
268-
_cancelled_gc.set(CANCELLABLE);
272+
_cancelled_gc.set(GCCause::_no_gc);
269273
if (_cancel_requested_time > 0) {
270274
log_debug(gc)("GC cancellation took %.3fs", (os::elapsedTime() - _cancel_requested_time));
271275
_cancel_requested_time = 0;

‎src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ const char* ShenandoahOldGeneration::state_name(State state) {
495495

496496
void ShenandoahOldGeneration::transition_to(State new_state) {
497497
if (_state != new_state) {
498-
log_debug(gc)("Old generation transition from %s to %s", state_name(_state), state_name(new_state));
498+
log_debug(gc, thread)("Old generation transition from %s to %s", state_name(_state), state_name(new_state));
499499
EventMark event("Old was %s, now is %s", state_name(_state), state_name(new_state));
500500
validate_transition(new_state);
501501
_state = new_state;

‎src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp

+17-14
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@
3333
#include "logging/log.hpp"
3434

3535
ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahGenerationalControlThread* control_thread) :
36-
ConcurrentGCThread(),
36+
_heap(ShenandoahHeap::heap()),
3737
_control_thread(control_thread),
3838
_sleep(ShenandoahControlIntervalMin),
3939
_last_sleep_adjust_time(os::elapsedTime()) {
4040
shenandoah_assert_generational();
41-
ShenandoahHeap* heap = ShenandoahHeap::heap();
42-
_old_heuristics = heap->old_generation()->heuristics();
43-
_young_heuristics = heap->young_generation()->heuristics();
44-
_global_heuristics = heap->global_generation()->heuristics();
41+
_old_heuristics = _heap->old_generation()->heuristics();
42+
_young_heuristics = _heap->young_generation()->heuristics();
43+
_global_heuristics = _heap->global_generation()->heuristics();
4544

4645
set_name("Shenandoah Regulator Thread");
4746
create_and_start();
@@ -62,7 +61,7 @@ void ShenandoahRegulatorThread::regulate_young_and_old_cycles() {
6261
ShenandoahGenerationalControlThread::GCMode mode = _control_thread->gc_mode();
6362
if (mode == ShenandoahGenerationalControlThread::none) {
6463
if (should_start_metaspace_gc()) {
65-
if (request_concurrent_gc(GLOBAL)) {
64+
if (request_concurrent_gc(_heap->global_generation())) {
6665
// Some of vmTestbase/metaspace tests depend on following line to count GC cycles
6766
_global_heuristics->log_trigger("%s", GCCause::to_string(GCCause::_metadata_GC_threshold));
6867
_global_heuristics->cancel_trigger_request();
@@ -75,10 +74,14 @@ void ShenandoahRegulatorThread::regulate_young_and_old_cycles() {
7574
log_debug(gc)("Heuristics request for old collection accepted");
7675
_young_heuristics->cancel_trigger_request();
7776
_old_heuristics->cancel_trigger_request();
78-
} else if (request_concurrent_gc(YOUNG)) {
77+
} else if (request_concurrent_gc(_heap->young_generation())) {
7978
log_debug(gc)("Heuristics request for young collection accepted");
8079
_young_heuristics->cancel_trigger_request();
8180
}
81+
} else if (_old_heuristics->should_resume_old_cycle() || _old_heuristics->should_start_gc()) {
82+
if (request_concurrent_gc(_heap->old_generation())) {
83+
log_debug(gc)("Heuristics request to resume old collection accepted");
84+
}
8285
}
8386
}
8487
} else if (mode == ShenandoahGenerationalControlThread::servicing_old) {
@@ -132,19 +135,19 @@ void ShenandoahRegulatorThread::regulator_sleep() {
132135
}
133136
}
134137

135-
bool ShenandoahRegulatorThread::start_old_cycle() {
136-
return _old_heuristics->should_start_gc() && request_concurrent_gc(OLD);
138+
bool ShenandoahRegulatorThread::start_old_cycle() const {
139+
return _old_heuristics->should_start_gc() && request_concurrent_gc(_heap->old_generation());
137140
}
138141

139-
bool ShenandoahRegulatorThread::start_young_cycle() {
140-
return _young_heuristics->should_start_gc() && request_concurrent_gc(YOUNG);
142+
bool ShenandoahRegulatorThread::start_young_cycle() const {
143+
return _young_heuristics->should_start_gc() && request_concurrent_gc(_heap->young_generation());
141144
}
142145

143-
bool ShenandoahRegulatorThread::start_global_cycle() {
144-
return _global_heuristics->should_start_gc() && request_concurrent_gc(GLOBAL);
146+
bool ShenandoahRegulatorThread::start_global_cycle() const {
147+
return _global_heuristics->should_start_gc() && request_concurrent_gc(_heap->global_generation());
145148
}
146149

147-
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGenerationType generation) {
150+
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const {
148151
double now = os::elapsedTime();
149152
bool accepted = _control_thread->request_concurrent_gc(generation);
150153
if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) {

‎src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.hpp

+10-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626

2727
#include "gc/shared/concurrentGCThread.hpp"
2828

29+
class ShenandoahHeap;
2930
class ShenandoahHeuristics;
31+
class ShenandoahGeneration;
3032
class ShenandoahGenerationalControlThread;
33+
class ShenandoahOldHeuristics;
3134

3235
/*
3336
* The purpose of this class (and thread) is to allow us to continue
@@ -58,9 +61,10 @@ class ShenandoahRegulatorThread: public ConcurrentGCThread {
5861
void regulate_young_and_global_cycles();
5962

6063
// These return true if a cycle was started.
61-
bool start_old_cycle();
62-
bool start_young_cycle();
63-
bool start_global_cycle();
64+
bool start_old_cycle() const;
65+
bool start_young_cycle() const;
66+
bool start_global_cycle() const;
67+
bool resume_old_cycle();
6468

6569
// The generational mode can only unload classes in a global cycle. The regulator
6670
// thread itself will trigger a global cycle if metaspace is out of memory.
@@ -70,11 +74,12 @@ class ShenandoahRegulatorThread: public ConcurrentGCThread {
7074
void regulator_sleep();
7175

7276
// Provides instrumentation to track how long it takes to acknowledge a request.
73-
bool request_concurrent_gc(ShenandoahGenerationType generation);
77+
bool request_concurrent_gc(ShenandoahGeneration* generation) const;
7478

79+
ShenandoahHeap* _heap;
7580
ShenandoahGenerationalControlThread* _control_thread;
7681
ShenandoahHeuristics* _young_heuristics;
77-
ShenandoahHeuristics* _old_heuristics;
82+
ShenandoahOldHeuristics* _old_heuristics;
7883
ShenandoahHeuristics* _global_heuristics;
7984

8085
uint _sleep;

‎src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ HeapWord* ShenandoahScanRemembered::addr_for_cluster(size_t cluster_no) {
475475
void ShenandoahScanRemembered::roots_do(OopIterateClosure* cl) {
476476
ShenandoahHeap* heap = ShenandoahHeap::heap();
477477
bool old_bitmap_stable = heap->old_generation()->is_mark_complete();
478-
log_info(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable));
478+
log_debug(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable));
479479
for (size_t i = 0, n = heap->num_regions(); i < n; ++i) {
480480
ShenandoahHeapRegion* region = heap->get_region(i);
481481
if (region->is_old() && region->is_active() && !region->is_cset()) {
@@ -653,7 +653,7 @@ ShenandoahScanRememberedTask::ShenandoahScanRememberedTask(ShenandoahObjToScanQu
653653
WorkerTask("Scan Remembered Set"),
654654
_queue_set(queue_set), _old_queue_set(old_queue_set), _rp(rp), _work_list(work_list), _is_concurrent(is_concurrent) {
655655
bool old_bitmap_stable = ShenandoahHeap::heap()->old_generation()->is_mark_complete();
656-
log_info(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable));
656+
log_debug(gc, remset)("Scan remembered set using bitmap: %s", BOOL_TO_STR(old_bitmap_stable));
657657
}
658658

659659
void ShenandoahScanRememberedTask::work(uint worker_id) {

0 commit comments

Comments
 (0)
Please sign in to comment.