Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generation resizing #177

Closed
Closed
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f6b9369
WIP: First cut of MMU tracker for Shenandoah
earthling-amzn Nov 9, 2022
d2e1ce7
WIP: Attribute thread CPU time to old or young gen collection
earthling-amzn Nov 10, 2022
72a7478
WIP: Attribute non-worker thread time when gc is idle to global gener…
earthling-amzn Nov 11, 2022
1d94a19
WIP: Track idle gc time and mmu averages, rename confusing method name
earthling-amzn Nov 12, 2022
3eb53d6
WIP: Transfer up to 10% capacity to undersized generation
earthling-amzn Nov 15, 2022
bc50b60
Limits on generation size adjustments, log young/old heap occupancy i…
earthling-amzn Nov 19, 2022
2a54600
Fix crash in SATB mode, always log average MMU on scheduled interval
earthling-amzn Dec 1, 2022
179c713
Adjust generation sizes from safepoint
earthling-amzn Dec 2, 2022
cd4cacf
Clamp adjustments to min/max when increment is too large
earthling-amzn Dec 2, 2022
f974b1b
Remove vestigial mmu tracker instance
earthling-amzn Dec 2, 2022
959bb05
Remove unused time between cycle tracking
earthling-amzn Dec 3, 2022
2718ba0
Revert unnecessary change
earthling-amzn Dec 3, 2022
b916a90
Document the class responsible for adjusting generation sizes
earthling-amzn Dec 3, 2022
03c2ebd
Merge from shenandoah/master
earthling-amzn Dec 5, 2022
41f057f
Remove unnecessary logging, clean up imports
earthling-amzn Dec 5, 2022
d7a0194
Remove vestigial lock, do not enroll periodic task while holding thre…
earthling-amzn Dec 5, 2022
50896e3
Merge branch 'shenandoah-master' into mmu-instrumentation
earthling-amzn Dec 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -419,7 +419,7 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() {
// ShenandoahControlIntervalMax. The current control interval (or the max control interval) should also be added into
// the calculation of avg_cycle_time below.

double avg_cycle_time = _gc_time_history->davg() + (_margin_of_error_sd * _gc_time_history->dsd());
double avg_cycle_time = _gc_cycle_time_history->davg() + (_margin_of_error_sd * _gc_cycle_time_history->dsd());

size_t last_live_memory = get_last_live_memory();
size_t penultimate_live_memory = get_penultimate_live_memory();
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahGeneration* generation) :
_last_cycle_end(0),
_gc_times_learned(0),
_gc_time_penalties(0),
_gc_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)),
_gc_cycle_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)),
_live_memory_last_cycle(0),
_live_memory_penultimate_cycle(0),
_metaspace_oom()
@@ -302,7 +302,7 @@ void ShenandoahHeuristics::record_success_concurrent(bool abbreviated) {
_successful_cycles_in_a_row++;

if (!(abbreviated && ShenandoahAdaptiveIgnoreShortCycles)) {
_gc_time_history->add(time_since_last_gc());
_gc_cycle_time_history->add(elapsed_cycle_time());
_gc_times_learned++;
}

@@ -361,7 +361,7 @@ void ShenandoahHeuristics::initialize() {
// Nothing to do by default.
}

double ShenandoahHeuristics::time_since_last_gc() const {
double ShenandoahHeuristics::elapsed_cycle_time() const {
return os::elapsedTime() - _cycle_start;
}

Original file line number Diff line number Diff line change
@@ -97,7 +97,7 @@ class ShenandoahHeuristics : public CHeapObj<mtGC> {

size_t _gc_times_learned;
intx _gc_time_penalties;
TruncatedSeq* _gc_time_history;
TruncatedSeq* _gc_cycle_time_history;

size_t _live_memory_last_cycle;
size_t _live_memory_penultimate_cycle;
@@ -168,7 +168,7 @@ class ShenandoahHeuristics : public CHeapObj<mtGC> {
virtual bool is_experimental() = 0;
virtual void initialize();

double time_since_last_gc() const;
double elapsed_cycle_time() const;

void save_last_live_memory(size_t live_memory);
size_t get_last_live_memory();
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ void ShenandoahGenerationalMode::initialize_flags() const {
FLAG_SET_DEFAULT(VerifyBeforeExit, false);
}

SHENANDOAH_ERGO_OVERRIDE_DEFAULT(GCTimeRatio, 70);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this translate to a GC overhead of 1/71*100% = 1.4%?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a confusingly named parameter, but I'm interpreting it based on the description:

"Adaptive size policy application time to GC time ratio"

Any time the average MMU drops below this number, it attempts to resize the generations.

SHENANDOAH_ERGO_OVERRIDE_DEFAULT(NewRatio, 1);
SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahUnloadClassesFrequency, 0);
SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent);
7 changes: 7 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
Original file line number Diff line number Diff line change
@@ -69,6 +69,13 @@ void ShenandoahArguments::initialize() {
FLAG_SET_DEFAULT(UseNUMA, true);
}

// We use this as the time period for tracking minimum mutator utilization (MMU).
// In generational mode, the MMU is used as a signal to adjust the size of the
// young generation.
if (FLAG_IS_DEFAULT(GCPauseIntervalMillis)) {
FLAG_SET_DEFAULT(GCPauseIntervalMillis, 5000);
}

// Set up default number of concurrent threads. We want to have cycles complete fast
// enough, but we also do not want to steal too much CPU from the concurrently running
// application. Using 1/4 of available threads for concurrent GC seems a good
4 changes: 4 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp
Original file line number Diff line number Diff line change
@@ -167,6 +167,9 @@ class ShenandoahAsserts {

#define shenandoah_assert_heaplocked_or_fullgc_safepoint() \
ShenandoahAsserts::assert_heaplocked_or_fullgc_safepoint(__FILE__, __LINE__)

#define shenandoah_assert_generational() \
assert(ShenandoahHeap::heap()->mode()->is_generational(), "Must be in generational mode here.")
#else
#define shenandoah_assert_in_heap(interior_loc, obj)
#define shenandoah_assert_in_heap_or_null(interior_loc, obj)
@@ -218,6 +221,7 @@ class ShenandoahAsserts {
#define shenandoah_assert_not_heaplocked()
#define shenandoah_assert_heaplocked_or_safepoint()
#define shenandoah_assert_heaplocked_or_fullgc_safepoint()
#define shenandoah_assert_generational()

#endif

Original file line number Diff line number Diff line change
@@ -52,14 +52,12 @@ class ShenandoahCollectorPolicy : public CHeapObj<mtGC> {
size_t _explicit_full;
size_t _implicit_concurrent;
size_t _implicit_full;
size_t _cycle_counter;
size_t _degen_points[ShenandoahGC::_DEGENERATED_LIMIT];

ShenandoahSharedFlag _in_shutdown;

ShenandoahTracer* _tracer;

size_t _cycle_counter;

public:
ShenandoahCollectorPolicy();

1 change: 1 addition & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
Original file line number Diff line number Diff line change
@@ -1202,6 +1202,7 @@ void ShenandoahConcurrentGC::op_final_updaterefs() {
}

heap->rebuild_free_set(true /*concurrent*/);
heap->adjust_generation_sizes();
}

void ShenandoahConcurrentGC::op_final_roots() {
17 changes: 11 additions & 6 deletions src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
Original file line number Diff line number Diff line change
@@ -448,7 +448,7 @@ void ShenandoahControlThread::process_phase_timings(const ShenandoahHeap* heap)
//
void ShenandoahControlThread::service_concurrent_normal_cycle(
const ShenandoahHeap* heap, const GenerationMode generation, GCCause::Cause cause) {

GCIdMark gc_id_mark;
switch (generation) {
case YOUNG: {
// Run a young cycle. This might or might not, have interrupted an ongoing
@@ -458,32 +458,29 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(
// that are in the cset.
log_info(gc, ergo)("Start GC cycle (YOUNG)");
service_concurrent_cycle(heap->young_generation(), cause, false);
heap->young_generation()->log_status();
break;
}
case GLOBAL: {
log_info(gc, ergo)("Start GC cycle (GLOBAL)");
service_concurrent_cycle(heap->global_generation(), cause, false);
heap->global_generation()->log_status();
break;
}
case OLD: {
log_info(gc, ergo)("Start GC cycle (OLD)");
service_concurrent_old_cycle(heap, cause);
heap->old_generation()->log_status();
break;
}
default:
ShouldNotReachHere();
}
log_heap_status(heap);
}

void ShenandoahControlThread::service_concurrent_old_cycle(const ShenandoahHeap* heap, GCCause::Cause &cause) {

ShenandoahOldGeneration* old_generation = heap->old_generation();
ShenandoahYoungGeneration* young_generation = heap->young_generation();

GCIdMark gc_id_mark;
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());

switch (old_generation->state()) {
@@ -582,6 +579,15 @@ bool ShenandoahControlThread::resume_concurrent_old_cycle(ShenandoahGeneration*
return true;
}

void ShenandoahControlThread::log_heap_status(const ShenandoahHeap* heap) {
if (heap->mode()->is_generational()) {
heap->young_generation()->log_status();
heap->old_generation()->log_status();
} else {
heap->global_generation()->log_status();
}
}

bool ShenandoahControlThread::check_soft_max_changed() const {
ShenandoahHeap* heap = ShenandoahHeap::heap();
size_t new_soft_max = Atomic::load(&SoftMaxHeapSize);
@@ -640,7 +646,6 @@ void ShenandoahControlThread::service_concurrent_cycle(ShenandoahGeneration* gen
if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) return;

ShenandoahHeap* heap = ShenandoahHeap::heap();
GCIdMark gc_id_mark;
ShenandoahGCSession session(cause, generation);
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());

2 changes: 2 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp
Original file line number Diff line number Diff line change
@@ -189,6 +189,8 @@ class ShenandoahControlThread: public ConcurrentGCThread {

void service_concurrent_cycle(const ShenandoahHeap* heap, ShenandoahGeneration* generation, GCCause::Cause &cause,
bool do_old_gc_bootstrap);

void log_heap_status(const ShenandoahHeap* heap);
};

#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLTHREAD_HPP
2 changes: 2 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
Original file line number Diff line number Diff line change
@@ -296,6 +296,8 @@ void ShenandoahDegenGC::op_degenerated() {
heap->set_old_evac_reserve(0);
heap->reset_old_evac_expended();
heap->set_promoted_reserve(0);

heap->adjust_generation_sizes();
}

if (ShenandoahVerify) {
2 changes: 2 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp
Original file line number Diff line number Diff line change
@@ -336,6 +336,8 @@ void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) {
// Resize metaspace
MetaspaceGC::compute_new_size();

heap->adjust_generation_sizes();

// Free worker slices
for (uint i = 0; i < heap->max_workers(); i++) {
delete worker_slices[i];
24 changes: 24 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
Original file line number Diff line number Diff line change
@@ -988,6 +988,19 @@ size_t ShenandoahGeneration::adjusted_unaffiliated_regions() const {
return (adjusted_capacity() - used_regions_size()) / ShenandoahHeapRegion::region_size_bytes();
}


void ShenandoahGeneration::increase_capacity(size_t increment) {
shenandoah_assert_heaplocked_or_safepoint();
_max_capacity += increment;
_soft_max_capacity += increment;
}

void ShenandoahGeneration::decrease_capacity(size_t decrement) {
shenandoah_assert_heaplocked_or_safepoint();
_max_capacity -= decrement;
_soft_max_capacity -= decrement;
}

void ShenandoahGeneration::record_success_concurrent(bool abbreviated) {
heuristics()->record_success_concurrent(abbreviated);
ShenandoahHeap::heap()->shenandoah_policy()->record_success_concurrent();
@@ -997,3 +1010,14 @@ void ShenandoahGeneration::record_success_degenerated() {
heuristics()->record_success_degenerated();
ShenandoahHeap::heap()->shenandoah_policy()->record_success_degenerated();
}

void ShenandoahGeneration::add_collection_time(double time_seconds) {
_collection_thread_time_s += time_seconds;
}

double ShenandoahGeneration::reset_collection_time() {
double t = _collection_thread_time_s;
_collection_thread_time_s = 0.0;
return t;
}

8 changes: 8 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@ class ShenandoahGeneration : public CHeapObj<mtGC> {

ShenandoahReferenceProcessor* const _ref_processor;

double _collection_thread_time_s;

protected:
// Usage
size_t _affiliated_region_count;
@@ -109,6 +111,9 @@ class ShenandoahGeneration : public CHeapObj<mtGC> {
void reset_bytes_allocated_since_gc_start();
void increase_allocated(size_t bytes);

void increase_capacity(size_t increment);
void decrease_capacity(size_t decrement);

void set_soft_max_capacity(size_t soft_max_capacity) {
_soft_max_capacity = soft_max_capacity;
}
@@ -181,6 +186,9 @@ class ShenandoahGeneration : public CHeapObj<mtGC> {

virtual void record_success_concurrent(bool abbreviated);
virtual void record_success_degenerated();

virtual void add_collection_time(double time_seconds);
double reset_collection_time();
};

#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP
37 changes: 37 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
Original file line number Diff line number Diff line change
@@ -625,6 +625,8 @@ class ShenandoahInitWorkerGCLABClosure : public ThreadClosure {

void ShenandoahHeap::post_initialize() {
CollectedHeap::post_initialize();
_mmu_tracker.initialize();

MutexLocker ml(Threads_lock);

ShenandoahInitWorkerGCLABClosure init_gclabs;
@@ -1093,6 +1095,13 @@ void ShenandoahHeap::coalesce_and_fill_old_regions() {
parallel_heap_region_iterate(&coalesce);
}

bool ShenandoahHeap::adjust_generation_sizes() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method idempotent? I guess it depends on the method of the same name in the MMU Tracker. I guess my question will be answered when I get to it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a related comment in MmuTracker::adjust_generational_size().

if (mode()->is_generational()) {
return _mmu_tracker.adjust_generation_sizes();
}
return false;
}

HeapWord* ShenandoahHeap::allocate_new_tlab(size_t min_size,
size_t requested_size,
size_t* actual_size) {
@@ -1741,6 +1750,7 @@ void ShenandoahHeap::prepare_for_verify() {

void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const {
tcl->do_thread(_control_thread);
tcl->do_thread(_regulator_thread);
workers()->threads_do(tcl);
if (_safepoint_workers != NULL) {
_safepoint_workers->threads_do(tcl);
@@ -1772,6 +1782,33 @@ void ShenandoahHeap::print_tracing_info() const {
}
}

void ShenandoahHeap::on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation) {
set_gc_cause(cause);
set_gc_generation(generation);

shenandoah_policy()->record_cycle_start();
generation->heuristics()->record_cycle_start();

// When a cycle starts, attribute any thread activity when the collector
// is idle to the global generation.
_mmu_tracker.record(global_generation());
}

void ShenandoahHeap::on_cycle_end(ShenandoahGeneration* generation) {
generation->heuristics()->record_cycle_end();

if (mode()->is_generational() &&
((generation->generation_mode() == GLOBAL) || upgraded_to_full())) {
// If we just completed a GLOBAL GC, claim credit for completion of young-gen and old-gen GC as well
young_generation()->heuristics()->record_cycle_end();
old_generation()->heuristics()->record_cycle_end();
}
set_gc_cause(GCCause::_no_gc);

// When a cycle ends, the thread activity is attributed to the respective generation
_mmu_tracker.record(generation);
}

void ShenandoahHeap::verify(VerifyOption vo) {
if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) {
if (ShenandoahVerify) {
6 changes: 6 additions & 0 deletions src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
#include "gc/shenandoah/shenandoahLock.hpp"
#include "gc/shenandoah/shenandoahEvacOOMHandler.hpp"
#include "gc/shenandoah/shenandoahEvacTracker.hpp"
#include "gc/shenandoah/shenandoahMmuTracker.hpp"
#include "gc/shenandoah/shenandoahPadding.hpp"
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
#include "gc/shenandoah/shenandoahUnload.hpp"
@@ -535,6 +536,7 @@ class ShenandoahHeap : public CollectedHeap {

ShenandoahPhaseTimings* _phase_timings;
ShenandoahEvacuationTracker* _evac_tracker;
ShenandoahMmuTracker _mmu_tracker;

ShenandoahControlThread* control_thread() { return _control_thread; }
ShenandoahRegulatorThread* regulator_thread() { return _regulator_thread; }
@@ -553,6 +555,9 @@ class ShenandoahHeap : public CollectedHeap {
ShenandoahPhaseTimings* phase_timings() const { return _phase_timings; }
ShenandoahEvacuationTracker* evac_tracker() const { return _evac_tracker; }

void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation);
void on_cycle_end(ShenandoahGeneration* generation);

ShenandoahVerifier* verifier();

// ---------- VM subsystem bindings
@@ -817,6 +822,7 @@ class ShenandoahHeap : public CollectedHeap {
void cancel_old_gc();
bool is_old_gc_active();
void coalesce_and_fill_old_regions();
bool adjust_generation_sizes();

// ---------- Helper functions
//
Loading