Skip to content

Commit 9a690ba

Browse files
author
William Kemper
committed
8349094: GenShen: Race between control and regulator threads may violate assertions
8352428: GenShen: Old-gen cycles are still looping 8352091: GenShen: assert(!(request.generation->is_old() && _heap->old_generation()->is_doing_mixed_evacuations())) failed: Old heuristic should not request cycles while it waits for mixed evacuation 8351464: Shenandoah: Hang on ShenandoahController::handle_alloc_failure when run test TestAllocHumongousFragment#generational 8352299: GenShen: Young cycles that interrupt old cycles cannot be cancelled Reviewed-by: kdnilsen Backport-of: 3a8a432
1 parent ae5737a commit 9a690ba

19 files changed

+685
-646
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class GCCause : public AllStatic {
7777

7878
_shenandoah_stop_vm,
7979
_shenandoah_allocation_failure_evac,
80+
_shenandoah_humongous_allocation_failure,
8081
_shenandoah_concurrent_gc,
8182
_shenandoah_upgrade_to_full_gc,
8283

src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ void ShenandoahHeuristics::record_cycle_end() {
188188

189189
bool ShenandoahHeuristics::should_start_gc() {
190190
if (_start_gc_is_pending) {
191+
log_trigger("GC start is already pending");
191192
return true;
192193
}
193194
// Perform GC to cleanup metaspace

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ int ShenandoahOldHeuristics::compare_by_index(RegionData a, RegionData b) {
6262
ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap) :
6363
ShenandoahHeuristics(generation),
6464
_heap(gen_heap),
65-
_old_gen(generation),
6665
_first_pinned_candidate(NOT_FOUND),
6766
_last_old_collection_candidate(0),
6867
_next_old_collection_candidate(0),
@@ -569,9 +568,9 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
569568
// allocation request will require a STW full GC.
570569
size_t allowed_old_gen_span = num_regions - (ShenandoahGenerationalHumongousReserve * num_regions) / 100;
571570

572-
size_t old_available = _old_gen->available() / HeapWordSize;
571+
size_t old_available = _old_generation->available() / HeapWordSize;
573572
size_t region_size_words = ShenandoahHeapRegion::region_size_words();
574-
size_t old_unaffiliated_available = _old_gen->free_unaffiliated_regions() * region_size_words;
573+
size_t old_unaffiliated_available = _old_generation->free_unaffiliated_regions() * region_size_words;
575574
assert(old_available >= old_unaffiliated_available, "sanity");
576575
size_t old_fragmented_available = old_available - old_unaffiliated_available;
577576

@@ -605,12 +604,12 @@ void ShenandoahOldHeuristics::set_trigger_if_old_is_fragmented(size_t first_old_
605604
}
606605

607606
void ShenandoahOldHeuristics::set_trigger_if_old_is_overgrown() {
608-
size_t old_used = _old_gen->used() + _old_gen->get_humongous_waste();
609-
size_t trigger_threshold = _old_gen->usage_trigger_threshold();
607+
size_t old_used = _old_generation->used() + _old_generation->get_humongous_waste();
608+
size_t trigger_threshold = _old_generation->usage_trigger_threshold();
610609
// Detects unsigned arithmetic underflow
611610
assert(old_used <= _heap->capacity(),
612611
"Old used (" SIZE_FORMAT ", " SIZE_FORMAT") must not be more than heap capacity (" SIZE_FORMAT ")",
613-
_old_gen->used(), _old_gen->get_humongous_waste(), _heap->capacity());
612+
_old_generation->used(), _old_generation->get_humongous_waste(), _heap->capacity());
614613
if (old_used > trigger_threshold) {
615614
_growth_trigger = true;
616615
}
@@ -622,13 +621,32 @@ void ShenandoahOldHeuristics::evaluate_triggers(size_t first_old_region, size_t
622621
set_trigger_if_old_is_overgrown();
623622
}
624623

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

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

Lines changed: 3 additions & 1 deletion
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/shenandoahCollectorPolicy.cpp

Lines changed: 24 additions & 5 deletions
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

Lines changed: 7 additions & 4 deletions
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

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ ShenandoahControlThread::ShenandoahControlThread() :
5151

5252
void ShenandoahControlThread::run_service() {
5353
ShenandoahHeap* const heap = ShenandoahHeap::heap();
54-
5554
const GCMode default_mode = concurrent_normal;
5655
const GCCause::Cause default_cause = GCCause::_shenandoah_concurrent_gc;
5756
int sleep = ShenandoahControlIntervalMin;
@@ -60,9 +59,14 @@ void ShenandoahControlThread::run_service() {
6059

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

@@ -164,8 +168,8 @@ void ShenandoahControlThread::run_service() {
164168
notify_gc_waiters();
165169
}
166170

167-
// If this was the allocation failure GC cycle, notify waiters about it
168-
if (alloc_failure_pending) {
171+
// If this cycle completed without being cancelled, notify waiters about it
172+
if (!heap->cancelled_gc()) {
169173
notify_alloc_failure_waiters();
170174
}
171175

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

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

337341
void ShenandoahControlThread::stop_service() {
338-
// Nothing to do here.
342+
ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_stop_vm);
339343
}
340344

341345
void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) {
@@ -364,6 +368,11 @@ void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
364368
}
365369

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

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

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "precompiled.hpp"
2525

2626
#include "gc/shared/gc_globals.hpp"
27+
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
2728
#include "gc/shenandoah/shenandoahController.hpp"
2829
#include "gc/shenandoah/shenandoahHeap.hpp"
2930
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
@@ -37,14 +38,6 @@ size_t ShenandoahController::reset_allocs_seen() {
3738
return Atomic::xchg(&_allocs_seen, (size_t)0, memory_order_relaxed);
3839
}
3940

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-
4841
void ShenandoahController::update_gc_id() {
4942
Atomic::inc(&_gc_id);
5043
}
@@ -53,60 +46,39 @@ size_t ShenandoahController::get_gc_id() {
5346
return Atomic::load(&_gc_id);
5447
}
5548

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

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

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

72-
7361
if (block) {
7462
MonitorLocker ml(&_alloc_failure_waiters_lock);
75-
while (is_alloc_failure_gc()) {
63+
while (!should_terminate() && ShenandoahCollectorPolicy::is_allocation_failure(heap->cancelled_cause())) {
7664
ml.wait();
7765
}
7866
}
7967
}
8068

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

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

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

9580
void ShenandoahController::notify_alloc_failure_waiters() {
96-
_alloc_failure_gc.unset();
97-
_humongous_alloc_failure_gc.unset();
9881
MonitorLocker ml(&_alloc_failure_waiters_lock);
9982
ml.notify_all();
10083
}
10184

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-
}
112-

0 commit comments

Comments
 (0)