Skip to content

Commit a2870d6

Browse files
committed
8368015: Shenandoah: fix error in computation of average allocation rate
Reviewed-by: wkemper
1 parent 17accf4 commit a2870d6

File tree

7 files changed

+74
-24
lines changed

7 files changed

+74
-24
lines changed

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,14 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() {
240240
log_debug(gc)("should_start_gc? available: %zu, soft_max_capacity: %zu"
241241
", allocated: %zu", available, capacity, allocated);
242242

243+
// Track allocation rate even if we decide to start a cycle for other reasons.
244+
double rate = _allocation_rate.sample(allocated);
245+
243246
if (_start_gc_is_pending) {
244247
log_trigger("GC start is already pending");
245248
return true;
246249
}
247250

248-
// Track allocation rate even if we decide to start a cycle for other reasons.
249-
double rate = _allocation_rate.sample(allocated);
250251
_last_trigger = OTHER;
251252

252253
size_t min_threshold = min_free_threshold();
@@ -360,16 +361,32 @@ ShenandoahAllocationRate::ShenandoahAllocationRate() :
360361
_rate_avg(int(ShenandoahAdaptiveSampleSizeSeconds * ShenandoahAdaptiveSampleFrequencyHz), ShenandoahAdaptiveDecayFactor) {
361362
}
362363

364+
double ShenandoahAllocationRate::force_sample(size_t allocated, size_t &unaccounted_bytes_allocated) {
365+
const double MinSampleTime = 0.002; // Do not sample if time since last update is less than 2 ms
366+
double now = os::elapsedTime();
367+
double time_since_last_update = now -_last_sample_time;
368+
if (time_since_last_update < MinSampleTime) {
369+
unaccounted_bytes_allocated = allocated - _last_sample_value;
370+
_last_sample_value = 0;
371+
return 0.0;
372+
} else {
373+
double rate = instantaneous_rate(now, allocated);
374+
_rate.add(rate);
375+
_rate_avg.add(_rate.avg());
376+
_last_sample_time = now;
377+
_last_sample_value = allocated;
378+
unaccounted_bytes_allocated = 0;
379+
return rate;
380+
}
381+
}
382+
363383
double ShenandoahAllocationRate::sample(size_t allocated) {
364384
double now = os::elapsedTime();
365385
double rate = 0.0;
366386
if (now - _last_sample_time > _interval_sec) {
367-
if (allocated >= _last_sample_value) {
368-
rate = instantaneous_rate(now, allocated);
369-
_rate.add(rate);
370-
_rate_avg.add(_rate.avg());
371-
}
372-
387+
rate = instantaneous_rate(now, allocated);
388+
_rate.add(rate);
389+
_rate_avg.add(_rate.avg());
373390
_last_sample_time = now;
374391
_last_sample_value = allocated;
375392
}

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ class ShenandoahAllocationRate : public CHeapObj<mtGC> {
3737
explicit ShenandoahAllocationRate();
3838
void allocation_counter_reset();
3939

40+
double force_sample(size_t allocated, size_t &unaccounted_bytes_allocated);
4041
double sample(size_t allocated);
4142

4243
double upper_bound(double sds) const;
4344
bool is_spiking(double rate, double threshold) const;
45+
4446
private:
4547

4648
double instantaneous_rate(double time, size_t allocated) const;
@@ -71,18 +73,18 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
7173

7274
virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset,
7375
RegionData* data, size_t size,
74-
size_t actual_free);
76+
size_t actual_free) override;
7577

76-
void record_cycle_start();
77-
void record_success_concurrent();
78-
void record_success_degenerated();
79-
void record_success_full();
78+
virtual void record_cycle_start() override;
79+
virtual void record_success_concurrent() override;
80+
virtual void record_success_degenerated() override;
81+
virtual void record_success_full() override;
8082

81-
virtual bool should_start_gc();
83+
virtual bool should_start_gc() override;
8284

83-
virtual const char* name() { return "Adaptive"; }
84-
virtual bool is_diagnostic() { return false; }
85-
virtual bool is_experimental() { return false; }
85+
virtual const char* name() override { return "Adaptive"; }
86+
virtual bool is_diagnostic() override { return false; }
87+
virtual bool is_experimental() override { return false; }
8688

8789
private:
8890
// These are used to adjust the margin of error and the spike threshold
@@ -150,6 +152,13 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
150152
_last_trigger = trigger_type;
151153
ShenandoahHeuristics::accept_trigger();
152154
}
155+
156+
public:
157+
virtual size_t force_alloc_rate_sample(size_t bytes_allocated) override {
158+
size_t unaccounted_bytes;
159+
_allocation_rate.force_sample(bytes_allocated, unaccounted_bytes);
160+
return unaccounted_bytes;
161+
}
153162
};
154163

155164
#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ class ShenandoahHeuristics : public CHeapObj<mtGC> {
241241

242242
double elapsed_cycle_time() const;
243243

244+
virtual size_t force_alloc_rate_sample(size_t bytes_allocated) {
245+
// do nothing
246+
return 0;
247+
}
248+
244249
// Format prefix and emit log message indicating a GC cycle hs been triggered
245250
void log_trigger(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3);
246251
};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class ShenandoahSpaceInfo {
4141
virtual size_t soft_available() const = 0;
4242
virtual size_t available() const = 0;
4343
virtual size_t used() const = 0;
44+
45+
// Return an approximation of the bytes allocated since GC start. The value returned is monotonically non-decreasing
46+
// in time within each GC cycle. For certain GC cycles, the value returned may include some bytes allocated before
47+
// the start of the current GC cycle.
4448
virtual size_t bytes_allocated_since_gc_start() const = 0;
4549
};
4650

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ size_t ShenandoahGeneration::bytes_allocated_since_gc_start() const {
151151
return AtomicAccess::load(&_bytes_allocated_since_gc_start);
152152
}
153153

154-
void ShenandoahGeneration::reset_bytes_allocated_since_gc_start() {
155-
AtomicAccess::store(&_bytes_allocated_since_gc_start, (size_t)0);
154+
void ShenandoahGeneration::reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated) {
155+
AtomicAccess::store(&_bytes_allocated_since_gc_start, initial_bytes_allocated);
156156
}
157157

158158
void ShenandoahGeneration::increase_allocated(size_t bytes) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ class ShenandoahGeneration : public CHeapObj<mtGC>, public ShenandoahSpaceInfo {
142142
size_t soft_available() const override;
143143

144144
size_t bytes_allocated_since_gc_start() const override;
145-
void reset_bytes_allocated_since_gc_start();
145+
void reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated);
146146
void increase_allocated(size_t bytes);
147147

148148
// These methods change the capacity of the generation by adding or subtracting the given number of bytes from the current

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

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,12 +2319,27 @@ address ShenandoahHeap::in_cset_fast_test_addr() {
23192319
}
23202320

23212321
void ShenandoahHeap::reset_bytes_allocated_since_gc_start() {
2322+
// It is important to force_alloc_rate_sample() before the associated generation's bytes_allocated has been reset.
2323+
// Note that there is no lock to prevent additional alloations between sampling bytes_allocated_since_gc_start() and
2324+
// reset_bytes_allocated_since_gc_start(). If additional allocations happen, they will be ignored in the average
2325+
// allocation rate computations. This effect is considered to be be negligible.
2326+
2327+
// unaccounted_bytes is the bytes not accounted for by our forced sample. If the sample interval is too short,
2328+
// the "forced sample" will not happen, and any recently allocated bytes are "unaccounted for". We pretend these
2329+
// bytes are allocated after the start of subsequent gc.
2330+
size_t unaccounted_bytes;
23222331
if (mode()->is_generational()) {
2323-
young_generation()->reset_bytes_allocated_since_gc_start();
2324-
old_generation()->reset_bytes_allocated_since_gc_start();
2332+
size_t bytes_allocated = young_generation()->bytes_allocated_since_gc_start();
2333+
unaccounted_bytes = young_generation()->heuristics()->force_alloc_rate_sample(bytes_allocated);
2334+
young_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
2335+
unaccounted_bytes = 0;
2336+
old_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
2337+
} else {
2338+
size_t bytes_allocated = global_generation()->bytes_allocated_since_gc_start();
2339+
// Single-gen Shenandoah uses global heuristics.
2340+
unaccounted_bytes = heuristics()->force_alloc_rate_sample(bytes_allocated);
23252341
}
2326-
2327-
global_generation()->reset_bytes_allocated_since_gc_start();
2342+
global_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
23282343
}
23292344

23302345
void ShenandoahHeap::set_degenerated_gc_in_progress(bool in_progress) {

0 commit comments

Comments
 (0)