Skip to content

Commit

Permalink
8311978: Shenandoah: Create abstraction over heap metrics for heuristics
Browse files Browse the repository at this point in the history
Reviewed-by: ysr, phh, shade
  • Loading branch information
William Kemper authored and Paul Hohensee committed Jul 21, 2023
1 parent 3c644dc commit bfa76df
Show file tree
Hide file tree
Showing 18 changed files with 148 additions and 44 deletions.
Expand Up @@ -54,8 +54,8 @@ const double ShenandoahAdaptiveHeuristics::HIGHEST_EXPECTED_AVAILABLE_AT_END = 0
const double ShenandoahAdaptiveHeuristics::MINIMUM_CONFIDENCE = 0.319; // 25%
const double ShenandoahAdaptiveHeuristics::MAXIMUM_CONFIDENCE = 3.291; // 99.9%

ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics() :
ShenandoahHeuristics(),
ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* space_info) :
ShenandoahHeuristics(space_info),
_margin_of_error_sd(ShenandoahAdaptiveInitialConfidence),
_spike_threshold_sd(ShenandoahAdaptiveInitialSpikeThreshold),
_last_trigger(OTHER) { }
Expand Down Expand Up @@ -84,7 +84,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand
// we hit max_cset. When max_cset is hit, we terminate the cset selection. Note that in this scheme,
// ShenandoahGarbageThreshold is the soft threshold which would be ignored until min_garbage is hit.

size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t max_cset = (size_t)((1.0 * capacity / 100 * ShenandoahEvacReserve) / ShenandoahEvacWaste);
size_t free_target = (capacity / 100 * ShenandoahMinFreeThreshold) + max_cset;
size_t min_garbage = (free_target > actual_free ? (free_target - actual_free) : 0);
Expand Down Expand Up @@ -128,7 +128,7 @@ void ShenandoahAdaptiveHeuristics::record_cycle_start() {
void ShenandoahAdaptiveHeuristics::record_success_concurrent() {
ShenandoahHeuristics::record_success_concurrent();

size_t available = ShenandoahHeap::heap()->free_set()->available();
size_t available = _space_info->available();

_available.add(available);
double z_score = 0.0;
Expand Down Expand Up @@ -196,11 +196,10 @@ static double saturate(double value, double min, double max) {
}

bool ShenandoahAdaptiveHeuristics::should_start_gc() {
ShenandoahHeap* heap = ShenandoahHeap::heap();
size_t max_capacity = heap->max_capacity();
size_t capacity = heap->soft_max_capacity();
size_t available = heap->free_set()->available();
size_t allocated = heap->bytes_allocated_since_gc_start();
size_t max_capacity = _space_info->max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t available = _space_info->available();
size_t allocated = _space_info->bytes_allocated_since_gc_start();

// Make sure the code below treats available without the soft tail.
size_t soft_tail = max_capacity - capacity;
Expand Down
Expand Up @@ -26,6 +26,7 @@
#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP

#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "utilities/numberSeq.hpp"

Expand All @@ -51,9 +52,20 @@ class ShenandoahAllocationRate : public CHeapObj<mtGC> {
TruncatedSeq _rate_avg;
};

/*
* The adaptive heuristic tracks the allocation behavior and average cycle
* time of the application. It attempts to start a cycle with enough time
* to complete before the available memory is exhausted. It errors on the
* side of starting cycles early to avoid allocation failures (degenerated
* cycles).
*
* This heuristic limits the number of regions for evacuation such that the
* evacuation reserve is respected. This helps it avoid allocation failures
* during evacuation. It preferentially selects regions with the most garbage.
*/
class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
public:
ShenandoahAdaptiveHeuristics();
ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* space_info);

virtual ~ShenandoahAdaptiveHeuristics();

Expand Down Expand Up @@ -99,11 +111,12 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
void adjust_margin_of_error(double amount);
void adjust_spike_threshold(double amount);

protected:
ShenandoahAllocationRate _allocation_rate;

// The margin of error expressed in standard deviations to add to our
// average cycle time and allocation rate. As this value increases we
// tend to over estimate the rate at which mutators will deplete the
// tend to overestimate the rate at which mutators will deplete the
// heap. In other words, erring on the side of caution will trigger more
// concurrent GCs.
double _margin_of_error_sd;
Expand Down
Expand Up @@ -31,7 +31,8 @@
#include "logging/logTag.hpp"
#include "runtime/os.hpp"

ShenandoahAggressiveHeuristics::ShenandoahAggressiveHeuristics() : ShenandoahHeuristics() {
ShenandoahAggressiveHeuristics::ShenandoahAggressiveHeuristics(ShenandoahSpaceInfo* space_info) :
ShenandoahHeuristics(space_info) {
// Do not shortcut evacuation
SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahImmediateThreshold, 100);

Expand Down
Expand Up @@ -27,9 +27,13 @@

#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"

/*
* This is a diagnostic heuristic that continuously runs collections
* cycles and adds every region with any garbage to the collection set.
*/
class ShenandoahAggressiveHeuristics : public ShenandoahHeuristics {
public:
ShenandoahAggressiveHeuristics();
ShenandoahAggressiveHeuristics(ShenandoahSpaceInfo* space_info);

virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset,
RegionData* data, size_t size,
Expand Down
Expand Up @@ -32,7 +32,8 @@
#include "logging/log.hpp"
#include "logging/logTag.hpp"

ShenandoahCompactHeuristics::ShenandoahCompactHeuristics() : ShenandoahHeuristics() {
ShenandoahCompactHeuristics::ShenandoahCompactHeuristics(ShenandoahSpaceInfo* space_info) :
ShenandoahHeuristics(space_info) {
SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent);
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent);
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahUncommit);
Expand All @@ -45,11 +46,9 @@ ShenandoahCompactHeuristics::ShenandoahCompactHeuristics() : ShenandoahHeuristic
}

bool ShenandoahCompactHeuristics::should_start_gc() {
ShenandoahHeap* heap = ShenandoahHeap::heap();

size_t max_capacity = heap->max_capacity();
size_t capacity = heap->soft_max_capacity();
size_t available = heap->free_set()->available();
size_t max_capacity = _space_info->max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t available = _space_info->available();

// Make sure the code below treats available without the soft tail.
size_t soft_tail = max_capacity - capacity;
Expand All @@ -65,7 +64,7 @@ bool ShenandoahCompactHeuristics::should_start_gc() {
return true;
}

size_t bytes_allocated = heap->bytes_allocated_since_gc_start();
size_t bytes_allocated = _space_info->bytes_allocated_since_gc_start();
if (bytes_allocated > threshold_bytes_allocated) {
log_info(gc)("Trigger: Allocated since last cycle (" SIZE_FORMAT "%s) is larger than allocation threshold (" SIZE_FORMAT "%s)",
byte_size_in_proper_unit(bytes_allocated), proper_unit_for_byte_size(bytes_allocated),
Expand Down
Expand Up @@ -27,9 +27,13 @@

#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"

/*
* This heuristic has simpler triggers than the adaptive heuristic. The
* size of the collection set is limited to 3/4 of available memory.
*/
class ShenandoahCompactHeuristics : public ShenandoahHeuristics {
public:
ShenandoahCompactHeuristics();
ShenandoahCompactHeuristics(ShenandoahSpaceInfo* space_info);

virtual bool should_start_gc();

Expand Down
Expand Up @@ -42,7 +42,8 @@ int ShenandoahHeuristics::compare_by_garbage(RegionData a, RegionData b) {
else return 0;
}

ShenandoahHeuristics::ShenandoahHeuristics() :
ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) :
_space_info(space_info),
_region_data(nullptr),
_degenerated_cycles_in_a_row(0),
_successful_cycles_in_a_row(0),
Expand Down
Expand Up @@ -25,6 +25,7 @@
#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHHEURISTICS_HPP
#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHHEURISTICS_HPP

#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp"
#include "gc/shenandoah/shenandoahHeap.hpp"
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahSharedVariables.hpp"
Expand Down Expand Up @@ -58,6 +59,11 @@
class ShenandoahCollectionSet;
class ShenandoahHeapRegion;

/*
* Shenandoah heuristics are primarily responsible for deciding when to start
* a collection cycle and choosing which regions will be evacuated during the
* cycle.
*/
class ShenandoahHeuristics : public CHeapObj<mtGC> {
static const intx Concurrent_Adjust = -1; // recover from penalties
static const intx Degenerated_Penalty = 10; // how much to penalize average GC duration history on Degenerated GC
Expand All @@ -69,6 +75,9 @@ class ShenandoahHeuristics : public CHeapObj<mtGC> {
size_t _garbage;
} RegionData;

// Source of information about the memory space managed by this heuristic
ShenandoahSpaceInfo* _space_info;

RegionData* _region_data;

uint _degenerated_cycles_in_a_row;
Expand All @@ -93,7 +102,7 @@ class ShenandoahHeuristics : public CHeapObj<mtGC> {
void adjust_penalty(intx step);

public:
ShenandoahHeuristics();
ShenandoahHeuristics(ShenandoahSpaceInfo* space_info);
virtual ~ShenandoahHeuristics();

void record_metaspace_oom() { _metaspace_oom.set(); }
Expand Down
Expand Up @@ -31,6 +31,9 @@
#include "logging/log.hpp"
#include "logging/logTag.hpp"

ShenandoahPassiveHeuristics::ShenandoahPassiveHeuristics(ShenandoahSpaceInfo* space_info) :
ShenandoahHeuristics(space_info) {}

bool ShenandoahPassiveHeuristics::should_start_gc() {
// Never do concurrent GCs.
return false;
Expand All @@ -53,7 +56,7 @@ void ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(Shenando

// Do not select too large CSet that would overflow the available free space.
// Take at least the entire evacuation reserve, and be free to overflow to free space.
size_t max_capacity = ShenandoahHeap::heap()->max_capacity();
size_t max_capacity = _space_info->max_capacity();
size_t available = MAX2(max_capacity / 100 * ShenandoahEvacReserve, actual_free);
size_t max_cset = (size_t)(available / ShenandoahEvacWaste);

Expand Down
Expand Up @@ -27,8 +27,19 @@

#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"

/*
* The passive heuristic is for use only with the passive mode. In
* the passive mode, Shenandoah only performs STW (i.e., degenerated)
* collections. All the barriers are disabled and there are no concurrent
* activities. Therefore, this heuristic _never_ triggers a cycle. It
* will select regions for evacuation based on ShenandoahEvacReserve,
* ShenandoahEvacWaste and ShenandoahGarbageThreshold. Note that it does
* not attempt to evacuate regions with more garbage.
*/
class ShenandoahPassiveHeuristics : public ShenandoahHeuristics {
public:
ShenandoahPassiveHeuristics(ShenandoahSpaceInfo* space_info);

virtual bool should_start_gc();

virtual bool should_unload_classes();
Expand Down
45 changes: 45 additions & 0 deletions src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
@@ -0,0 +1,45 @@
/*
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSPACEINFO_HPP
#define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSPACEINFO_HPP

#include "utilities/globalDefinitions.hpp"

/*
* The purpose of this interface is to decouple the heuristics from a
* direct dependency on the ShenandoahHeap singleton instance. This is
* done to facilitate future unit testing of the heuristics and to support
* future operational modes of Shenandoah in which the heap may be split
* into generations.
*/
class ShenandoahSpaceInfo {
public:
virtual size_t soft_max_capacity() const = 0;
virtual size_t max_capacity() const = 0;
virtual size_t available() const = 0;
virtual size_t bytes_allocated_since_gc_start() const = 0;
};

#endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSPACEINFO_HPP
Expand Up @@ -32,19 +32,18 @@
#include "logging/log.hpp"
#include "logging/logTag.hpp"

ShenandoahStaticHeuristics::ShenandoahStaticHeuristics() : ShenandoahHeuristics() {
ShenandoahStaticHeuristics::ShenandoahStaticHeuristics(ShenandoahSpaceInfo* space_info) :
ShenandoahHeuristics(space_info) {
SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent);
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent);
}

ShenandoahStaticHeuristics::~ShenandoahStaticHeuristics() {}

bool ShenandoahStaticHeuristics::should_start_gc() {
ShenandoahHeap* heap = ShenandoahHeap::heap();

size_t max_capacity = heap->max_capacity();
size_t capacity = heap->soft_max_capacity();
size_t available = heap->free_set()->available();
size_t max_capacity = _space_info->max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t available = _space_info->available();

// Make sure the code below treats available without the soft tail.
size_t soft_tail = max_capacity - capacity;
Expand Down
Expand Up @@ -27,9 +27,14 @@

#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"

/*
* The static heuristic will trigger cycles if the available memory falls
* below ShenandoahMinFreeThreshold percentage of total capacity. This
* heuristic will attempt to evacuation any region with any garbage.
*/
class ShenandoahStaticHeuristics : public ShenandoahHeuristics {
public:
ShenandoahStaticHeuristics();
ShenandoahStaticHeuristics(ShenandoahSpaceInfo* space_info);

virtual ~ShenandoahStaticHeuristics();

Expand Down
10 changes: 6 additions & 4 deletions src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp
Expand Up @@ -28,6 +28,7 @@
#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp"
#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp"
#include "gc/shenandoah/mode/shenandoahIUMode.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
#include "runtime/globals_extension.hpp"
Expand Down Expand Up @@ -67,14 +68,15 @@ ShenandoahHeuristics* ShenandoahIUMode::initialize_heuristics() const {
if (ShenandoahGCHeuristics == nullptr) {
vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)");
}
ShenandoahHeap* heap = ShenandoahHeap::heap();
if (strcmp(ShenandoahGCHeuristics, "aggressive") == 0) {
return new ShenandoahAggressiveHeuristics();
return new ShenandoahAggressiveHeuristics(heap);
} else if (strcmp(ShenandoahGCHeuristics, "static") == 0) {
return new ShenandoahStaticHeuristics();
return new ShenandoahStaticHeuristics(heap);
} else if (strcmp(ShenandoahGCHeuristics, "adaptive") == 0) {
return new ShenandoahAdaptiveHeuristics();
return new ShenandoahAdaptiveHeuristics(heap);
} else if (strcmp(ShenandoahGCHeuristics, "compact") == 0) {
return new ShenandoahCompactHeuristics();
return new ShenandoahCompactHeuristics(heap);
}
vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option");
return nullptr;
Expand Down
Expand Up @@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp"
#include "gc/shenandoah/mode/shenandoahPassiveMode.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
#include "runtime/globals_extension.hpp"
Expand Down Expand Up @@ -59,5 +60,5 @@ ShenandoahHeuristics* ShenandoahPassiveMode::initialize_heuristics() const {
if (ShenandoahGCHeuristics == nullptr) {
vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)");
}
return new ShenandoahPassiveHeuristics();
return new ShenandoahPassiveHeuristics(ShenandoahHeap::heap());
}

1 comment on commit bfa76df

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.