Skip to content

Commit 7fccea8

Browse files
author
William Kemper
committed
8368152: Shenandoah: Incorrect behavior at end of degenerated cycle
Reviewed-by: kdnilsen Backport-of: f36c33c86df0400d2155bfadd9a6b5ea56743133
1 parent d6e3334 commit 7fccea8

File tree

5 files changed

+126
-33
lines changed

5 files changed

+126
-33
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: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, Shenandoa
5151
ShenandoahGC(),
5252
_degen_point(degen_point),
5353
_generation(generation),
54-
_abbreviated(false),
55-
_consecutive_degen_with_bad_progress(0) {
54+
_abbreviated(false) {
5655
}
5756

5857
bool ShenandoahDegenGC::collect(GCCause::Cause cause) {
@@ -249,7 +248,6 @@ void ShenandoahDegenGC::op_degenerated() {
249248
ShenandoahHeapRegion* r;
250249
while ((r = heap->collection_set()->next()) != nullptr) {
251250
if (r->is_pinned()) {
252-
heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
253251
op_degenerated_fail();
254252
return;
255253
}
@@ -314,30 +312,14 @@ void ShenandoahDegenGC::op_degenerated() {
314312

315313
metrics.snap_after();
316314

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

@@ -485,7 +467,9 @@ const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) c
485467

486468
void ShenandoahDegenGC::upgrade_to_full() {
487469
log_info(gc)("Degenerated GC upgrading to Full GC");
488-
ShenandoahHeap::heap()->shenandoah_policy()->record_degenerated_upgrade_to_full();
470+
ShenandoahHeap* heap = ShenandoahHeap::heap();
471+
heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
472+
heap->shenandoah_policy()->record_degenerated_upgrade_to_full();
489473
ShenandoahFullGC full_gc;
490474
full_gc.op_full(GCCause::_shenandoah_upgrade_to_full_gc);
491475
}

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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 "precompiled.hpp"
26+
27+
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
28+
#include "unittest.hpp"
29+
30+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_sanity) {
31+
ShenandoahCollectorPolicy policy;
32+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
33+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
34+
}
35+
36+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_no_upgrade) {
37+
ShenandoahCollectorPolicy policy;
38+
policy.record_degenerated(true, true, true);
39+
policy.record_degenerated(true, true, true);
40+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
41+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
42+
}
43+
44+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_upgrade) {
45+
ShenandoahCollectorPolicy policy;
46+
policy.record_degenerated(true, true, false);
47+
policy.record_degenerated(true, true, false);
48+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
49+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), true);
50+
}
51+
52+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset_progress) {
53+
ShenandoahCollectorPolicy policy;
54+
policy.record_degenerated(true, true, false);
55+
policy.record_degenerated(true, true, true);
56+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL);
57+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
58+
}
59+
60+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_full_reset) {
61+
ShenandoahCollectorPolicy policy;
62+
policy.record_degenerated(true, true, false);
63+
policy.record_success_full();
64+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
65+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
66+
}
67+
68+
TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset) {
69+
ShenandoahCollectorPolicy policy;
70+
policy.record_degenerated(true, true, false);
71+
policy.record_success_concurrent(true, true);
72+
EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL);
73+
EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false);
74+
}

0 commit comments

Comments
 (0)