Skip to content

Commit f36c33c

Browse files
author
William Kemper
committed
8368152: Shenandoah: Incorrect behavior at end of degenerated cycle
Reviewed-by: kdnilsen, ysr
1 parent f68cba3 commit f36c33c

File tree

5 files changed

+122
-32
lines changed

5 files changed

+122
-32
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ShenandoahCollectorPolicy::ShenandoahCollectorPolicy() :
3737
_abbreviated_degenerated_gcs(0),
3838
_success_full_gcs(0),
3939
_consecutive_degenerated_gcs(0),
40+
_consecutive_degenerated_gcs_without_progress(0),
4041
_consecutive_young_gcs(0),
4142
_mixed_gcs(0),
4243
_success_old_gcs(0),
@@ -67,14 +68,14 @@ void ShenandoahCollectorPolicy::record_alloc_failure_to_degenerated(ShenandoahGC
6768
}
6869

6970
void ShenandoahCollectorPolicy::record_degenerated_upgrade_to_full() {
70-
_consecutive_degenerated_gcs = 0;
71+
reset_consecutive_degenerated_gcs();
7172
_alloc_failure_degenerated_upgrade_to_full++;
7273
}
7374

7475
void ShenandoahCollectorPolicy::record_success_concurrent(bool is_young, bool is_abbreviated) {
7576
update_young(is_young);
7677

77-
_consecutive_degenerated_gcs = 0;
78+
reset_consecutive_degenerated_gcs();
7879
_success_concurrent_gcs++;
7980
if (is_abbreviated) {
8081
_abbreviated_concurrent_gcs++;
@@ -95,11 +96,18 @@ void ShenandoahCollectorPolicy::record_interrupted_old() {
9596
_interrupted_old_gcs++;
9697
}
9798

98-
void ShenandoahCollectorPolicy::record_success_degenerated(bool is_young, bool is_abbreviated) {
99+
void ShenandoahCollectorPolicy::record_degenerated(bool is_young, bool is_abbreviated, bool progress) {
99100
update_young(is_young);
100101

101102
_success_degenerated_gcs++;
102103
_consecutive_degenerated_gcs++;
104+
105+
if (progress) {
106+
_consecutive_degenerated_gcs_without_progress = 0;
107+
} else {
108+
_consecutive_degenerated_gcs_without_progress++;
109+
}
110+
103111
if (is_abbreviated) {
104112
_abbreviated_degenerated_gcs++;
105113
}
@@ -114,7 +122,7 @@ void ShenandoahCollectorPolicy::update_young(bool is_young) {
114122
}
115123

116124
void ShenandoahCollectorPolicy::record_success_full() {
117-
_consecutive_degenerated_gcs = 0;
125+
reset_consecutive_degenerated_gcs();
118126
_consecutive_young_gcs = 0;
119127
_success_full_gcs++;
120128
}

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
4242
// Written by control thread, read by mutators
4343
volatile size_t _success_full_gcs;
4444
uint _consecutive_degenerated_gcs;
45+
uint _consecutive_degenerated_gcs_without_progress;
4546
volatile size_t _consecutive_young_gcs;
4647
size_t _mixed_gcs;
4748
size_t _success_old_gcs;
@@ -55,8 +56,25 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
5556
ShenandoahSharedFlag _in_shutdown;
5657
ShenandoahTracer* _tracer;
5758

59+
void reset_consecutive_degenerated_gcs() {
60+
_consecutive_degenerated_gcs = 0;
61+
_consecutive_degenerated_gcs_without_progress = 0;
62+
}
5863

5964
public:
65+
// The most common scenario for lack of good progress following a degenerated GC is an accumulation of floating
66+
// garbage during the most recently aborted concurrent GC effort. With generational GC, it is far more effective to
67+
// reclaim this floating garbage with another degenerated cycle (which focuses on young generation and might require
68+
// a pause of 200 ms) rather than a full GC cycle (which may require over 2 seconds with a 10 GB old generation).
69+
//
70+
// In generational mode, we'll only upgrade to full GC if we've done two degen cycles in a row and both indicated
71+
// bad progress. In non-generational mode, we'll preserve the original behavior, which is to upgrade to full
72+
// immediately following a degenerated cycle with bad progress. This preserves original behavior of non-generational
73+
// Shenandoah to avoid introducing "surprising new behavior." It also makes less sense with non-generational
74+
// Shenandoah to replace a full GC with a degenerated GC, because both have similar pause times in non-generational
75+
// mode.
76+
static constexpr size_t GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD = 2;
77+
6078
ShenandoahCollectorPolicy();
6179

6280
void record_mixed_cycle();
@@ -69,7 +87,12 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
6987
// cycles are very efficient and are worth tracking. Note that both degenerated and
7088
// concurrent cycles can be abbreviated.
7189
void record_success_concurrent(bool is_young, bool is_abbreviated);
72-
void record_success_degenerated(bool is_young, bool is_abbreviated);
90+
91+
// Record that a degenerated cycle has been completed. Note that such a cycle may or
92+
// may not make "progress". We separately track the total number of degenerated cycles,
93+
// the number of consecutive degenerated cycles and the number of consecutive cycles that
94+
// fail to make good progress.
95+
void record_degenerated(bool is_young, bool is_abbreviated, bool progress);
7396
void record_success_full();
7497
void record_alloc_failure_to_degenerated(ShenandoahGC::ShenandoahDegenPoint point);
7598
void record_alloc_failure_to_full();
@@ -94,6 +117,11 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
94117
return _consecutive_degenerated_gcs;
95118
}
96119

120+
// Genshen will only upgrade to a full gc after the configured number of futile degenerated cycles.
121+
bool generational_should_upgrade_degenerated_gc() const {
122+
return _consecutive_degenerated_gcs_without_progress >= GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD;
123+
}
124+
97125
static bool is_allocation_failure(GCCause::Cause cause);
98126
static bool is_shenandoah_gc(GCCause::Cause cause);
99127
static bool is_requested_gc(GCCause::Cause cause);

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

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, Shenandoa
4949
ShenandoahGC(),
5050
_degen_point(degen_point),
5151
_generation(generation),
52-
_abbreviated(false),
53-
_consecutive_degen_with_bad_progress(0) {
52+
_abbreviated(false) {
5453
}
5554

5655
bool ShenandoahDegenGC::collect(GCCause::Cause cause) {
@@ -247,7 +246,6 @@ void ShenandoahDegenGC::op_degenerated() {
247246
ShenandoahHeapRegion* r;
248247
while ((r = heap->collection_set()->next()) != nullptr) {
249248
if (r->is_pinned()) {
250-
heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
251249
op_degenerated_fail();
252250
return;
253251
}
@@ -312,30 +310,14 @@ void ShenandoahDegenGC::op_degenerated() {
312310

313311
metrics.snap_after();
314312

315-
// The most common scenario for lack of good progress following a degenerated GC is an accumulation of floating
316-
// garbage during the most recently aborted concurrent GC effort. With generational GC, it is far more effective to
317-
// reclaim this floating garbage with another degenerated cycle (which focuses on young generation and might require
318-
// a pause of 200 ms) rather than a full GC cycle (which may require over 2 seconds with a 10 GB old generation).
319-
//
320-
// In generational mode, we'll only upgrade to full GC if we've done two degen cycles in a row and both indicated
321-
// bad progress. In non-generational mode, we'll preserve the original behavior, which is to upgrade to full
322-
// immediately following a degenerated cycle with bad progress. This preserves original behavior of non-generational
323-
// Shenandoah so as to avoid introducing "surprising new behavior." It also makes less sense with non-generational
324-
// Shenandoah to replace a full GC with a degenerated GC, because both have similar pause times in non-generational
325-
// mode.
326-
if (!metrics.is_good_progress(_generation)) {
327-
_consecutive_degen_with_bad_progress++;
328-
} else {
329-
_consecutive_degen_with_bad_progress = 0;
330-
}
331-
if (!heap->mode()->is_generational() ||
332-
((heap->shenandoah_policy()->consecutive_degenerated_gc_count() > 1) && (_consecutive_degen_with_bad_progress >= 2))) {
333-
heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
334-
op_degenerated_futile();
335-
} else {
313+
// Decide if this cycle made good progress, and, if not, should it upgrade to a full GC.
314+
const bool progress = metrics.is_good_progress(_generation);
315+
ShenandoahCollectorPolicy* policy = heap->shenandoah_policy();
316+
policy->record_degenerated(_generation->is_young(), _abbreviated, progress);
317+
if (progress) {
336318
heap->notify_gc_progress();
337-
heap->shenandoah_policy()->record_success_degenerated(_generation->is_young(), _abbreviated);
338-
_generation->heuristics()->record_success_degenerated();
319+
} else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) {
320+
op_degenerated_futile();
339321
}
340322
}
341323

@@ -483,6 +465,7 @@ const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) c
483465
void ShenandoahDegenGC::upgrade_to_full() {
484466
log_info(gc)("Degenerated GC upgrading to Full GC");
485467
ShenandoahHeap* heap = ShenandoahHeap::heap();
468+
heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
486469
heap->increment_total_collections(true);
487470
heap->shenandoah_policy()->record_degenerated_upgrade_to_full();
488471
ShenandoahFullGC full_gc;

src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ class ShenandoahDegenGC : public ShenandoahGC {
3636
const ShenandoahDegenPoint _degen_point;
3737
ShenandoahGeneration* _generation;
3838
bool _abbreviated;
39-
size_t _consecutive_degen_with_bad_progress;
4039

4140
public:
4241
ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
26+
#include "unittest.hpp"
27+
28+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_sanity) {
29+
ShenandoahCollectorPolicy policy;
30+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
31+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
32+
}
33+
34+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_no_upgrade) {
35+
ShenandoahCollectorPolicy policy;
36+
policy.record_degenerated(true, true, true);
37+
policy.record_degenerated(true, true, true);
38+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
39+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
40+
}
41+
42+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_upgrade) {
43+
ShenandoahCollectorPolicy policy;
44+
policy.record_degenerated(true, true, false);
45+
policy.record_degenerated(true, true, false);
46+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
47+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), true);
48+
}
49+
50+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset_progress) {
51+
ShenandoahCollectorPolicy policy;
52+
policy.record_degenerated(true, true, false);
53+
policy.record_degenerated(true, true, true);
54+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
55+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
56+
}
57+
58+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_full_reset) {
59+
ShenandoahCollectorPolicy policy;
60+
policy.record_degenerated(true, true, false);
61+
policy.record_success_full();
62+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
63+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
64+
}
65+
66+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset) {
67+
ShenandoahCollectorPolicy policy;
68+
policy.record_degenerated(true, true, false);
69+
policy.record_success_concurrent(true, true);
70+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
71+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
72+
}

0 commit comments

Comments
 (0)