8358529: GenShen: Heuristics do not respond to changes in SoftMaxHeapSize

Reviewed-by: wkemper
This commit is contained in:
Rui Li 2025-07-08 18:34:18 +00:00 committed by William Kemper
parent 91df797879
commit fa32bfe113
17 changed files with 156 additions and 110 deletions

View File

@ -91,7 +91,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 = _space_info->soft_max_capacity();
size_t capacity = ShenandoahHeap::heap()->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);
@ -233,7 +233,7 @@ static double saturate(double value, double min, double max) {
// in operation mode. We want some way to decide that the average rate has changed, while keeping average
// allocation rate computation independent.
bool ShenandoahAdaptiveHeuristics::should_start_gc() {
size_t capacity = _space_info->soft_max_capacity();
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->soft_available();
size_t allocated = _space_info->bytes_allocated_since_gc_start();

View File

@ -47,7 +47,7 @@ ShenandoahCompactHeuristics::ShenandoahCompactHeuristics(ShenandoahSpaceInfo* sp
bool ShenandoahCompactHeuristics::should_start_gc() {
size_t max_capacity = _space_info->max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->available();
// Make sure the code below treats available without the soft tail.

View File

@ -37,7 +37,6 @@
class ShenandoahSpaceInfo {
public:
virtual const char* name() const = 0;
virtual size_t soft_max_capacity() const = 0;
virtual size_t max_capacity() const = 0;
virtual size_t soft_available() const = 0;
virtual size_t available() const = 0;

View File

@ -42,7 +42,7 @@ ShenandoahStaticHeuristics::~ShenandoahStaticHeuristics() {}
bool ShenandoahStaticHeuristics::should_start_gc() {
size_t max_capacity = _space_info->max_capacity();
size_t capacity = _space_info->soft_max_capacity();
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->available();
// Make sure the code below treats available without the soft tail.

View File

@ -183,7 +183,7 @@ void ShenandoahGeneration::log_status(const char *msg) const {
// byte size in proper unit and proper unit for byte size are consistent.
const size_t v_used = used();
const size_t v_used_regions = used_regions_size();
const size_t v_soft_max_capacity = soft_max_capacity();
const size_t v_soft_max_capacity = ShenandoahHeap::heap()->soft_max_capacity();
const size_t v_max_capacity = max_capacity();
const size_t v_available = available();
const size_t v_humongous_waste = get_humongous_waste();
@ -799,14 +799,13 @@ void ShenandoahGeneration::cancel_marking() {
ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type,
uint max_workers,
size_t max_capacity,
size_t soft_max_capacity) :
size_t max_capacity) :
_type(type),
_task_queues(new ShenandoahObjToScanQueueSet(max_workers)),
_ref_processor(new ShenandoahReferenceProcessor(MAX2(max_workers, 1U))),
_affiliated_region_count(0), _humongous_waste(0), _evacuation_reserve(0),
_used(0), _bytes_allocated_since_gc_start(0),
_max_capacity(max_capacity), _soft_max_capacity(soft_max_capacity),
_max_capacity(max_capacity),
_heuristics(nullptr)
{
_is_marking_complete.set();
@ -952,7 +951,7 @@ size_t ShenandoahGeneration::available_with_reserve() const {
}
size_t ShenandoahGeneration::soft_available() const {
return available(soft_max_capacity());
return available(ShenandoahHeap::heap()->soft_max_capacity());
}
size_t ShenandoahGeneration::available(size_t capacity) const {

View File

@ -71,7 +71,6 @@ protected:
volatile size_t _used;
volatile size_t _bytes_allocated_since_gc_start;
size_t _max_capacity;
size_t _soft_max_capacity;
ShenandoahHeuristics* _heuristics;
@ -105,8 +104,7 @@ private:
public:
ShenandoahGeneration(ShenandoahGenerationType type,
uint max_workers,
size_t max_capacity,
size_t soft_max_capacity);
size_t max_capacity);
~ShenandoahGeneration();
bool is_young() const { return _type == YOUNG; }
@ -126,7 +124,6 @@ private:
virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode);
size_t soft_max_capacity() const override { return _soft_max_capacity; }
size_t max_capacity() const override { return _max_capacity; }
virtual size_t used_regions() const;
virtual size_t used_regions_size() const;

View File

@ -53,21 +53,6 @@ public:
ShenandoahGenerationalInitLogger logger;
logger.print_all();
}
void print_heap() override {
ShenandoahInitLogger::print_heap();
ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
ShenandoahYoungGeneration* young = heap->young_generation();
log_info(gc, init)("Young Generation Soft Size: " EXACTFMT, EXACTFMTARGS(young->soft_max_capacity()));
log_info(gc, init)("Young Generation Max: " EXACTFMT, EXACTFMTARGS(young->max_capacity()));
ShenandoahOldGeneration* old = heap->old_generation();
log_info(gc, init)("Old Generation Soft Size: " EXACTFMT, EXACTFMTARGS(old->soft_max_capacity()));
log_info(gc, init)("Old Generation Max: " EXACTFMT, EXACTFMTARGS(old->max_capacity()));
}
protected:
void print_gc_specific() override {
ShenandoahInitLogger::print_gc_specific();
@ -141,8 +126,8 @@ void ShenandoahGenerationalHeap::initialize_heuristics() {
size_t initial_capacity_old = max_capacity() - max_capacity_young;
size_t max_capacity_old = max_capacity() - initial_capacity_young;
_young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young, initial_capacity_young);
_old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity_old, initial_capacity_old);
_young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young);
_old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity_old);
_young_generation->initialize_heuristics(mode());
_old_generation->initialize_heuristics(mode());
}

View File

@ -50,10 +50,6 @@ size_t ShenandoahGlobalGeneration::used_regions_size() const {
return ShenandoahHeap::heap()->capacity();
}
size_t ShenandoahGlobalGeneration::soft_max_capacity() const {
return ShenandoahHeap::heap()->soft_max_capacity();
}
size_t ShenandoahGlobalGeneration::available() const {
// The collector reserve may eat into what the mutator is allowed to use. Make sure we are looking
// at what is available to the mutator when reporting how much memory is available.
@ -65,8 +61,8 @@ size_t ShenandoahGlobalGeneration::soft_available() const {
size_t available = this->available();
// Make sure the code below treats available without the soft tail.
assert(max_capacity() >= soft_max_capacity(), "Max capacity must be greater than soft max capacity.");
size_t soft_tail = max_capacity() - soft_max_capacity();
assert(max_capacity() >= ShenandoahHeap::heap()->soft_max_capacity(), "Max capacity must be greater than soft max capacity.");
size_t soft_tail = max_capacity() - ShenandoahHeap::heap()->soft_max_capacity();
return (available > soft_tail) ? (available - soft_tail) : 0;
}

View File

@ -32,14 +32,13 @@
// A "generation" that represents the whole heap.
class ShenandoahGlobalGeneration : public ShenandoahGeneration {
public:
ShenandoahGlobalGeneration(bool generational, uint max_queues, size_t max_capacity, size_t soft_max_capacity)
: ShenandoahGeneration(generational ? GLOBAL : NON_GEN, max_queues, max_capacity, soft_max_capacity) { }
ShenandoahGlobalGeneration(bool generational, uint max_queues, size_t max_capacity)
: ShenandoahGeneration(generational ? GLOBAL : NON_GEN, max_queues, max_capacity) { }
public:
const char* name() const override;
size_t max_capacity() const override;
size_t soft_max_capacity() const override;
size_t used_regions() const override;
size_t used_regions_size() const override;
size_t available() const override;

View File

@ -32,6 +32,7 @@
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
#include "gc/shared/gc_globals.hpp"
#include "gc/shared/locationPrinter.inline.hpp"
#include "gc/shared/memAllocator.hpp"
#include "gc/shared/plab.hpp"
@ -201,8 +202,7 @@ jint ShenandoahHeap::initialize() {
assert(num_min_regions <= _num_regions, "sanity");
_minimum_size = num_min_regions * reg_size_bytes;
// Default to max heap size.
_soft_max_size = _num_regions * reg_size_bytes;
_soft_max_size = SoftMaxHeapSize;
_committed = _initial_size;
@ -524,7 +524,7 @@ void ShenandoahHeap::initialize_mode() {
}
void ShenandoahHeap::initialize_heuristics() {
_global_generation = new ShenandoahGlobalGeneration(mode()->is_generational(), max_workers(), max_capacity(), max_capacity());
_global_generation = new ShenandoahGlobalGeneration(mode()->is_generational(), max_workers(), max_capacity());
_global_generation->initialize_heuristics(mode());
}

View File

@ -43,6 +43,7 @@ void ShenandoahInitLogger::print_heap() {
log_info(gc, init)("Heap Region Count: %zu", ShenandoahHeapRegion::region_count());
log_info(gc, init)("Heap Region Size: " EXACTFMT, EXACTFMTARGS(ShenandoahHeapRegion::region_size_bytes()));
log_info(gc, init)("TLAB Size Max: " EXACTFMT, EXACTFMTARGS(ShenandoahHeapRegion::max_tlab_size_bytes()));
log_info(gc, init)("Soft Max Heap Size: " EXACTFMT, EXACTFMTARGS(ShenandoahHeap::heap()->soft_max_capacity()));
}
void ShenandoahInitLogger::print_gc_specific() {

View File

@ -196,8 +196,8 @@ public:
}
};
ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity)
: ShenandoahGeneration(OLD, max_queues, max_capacity, soft_max_capacity),
ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_capacity)
: ShenandoahGeneration(OLD, max_queues, max_capacity),
_coalesce_and_fill_region_array(NEW_C_HEAP_ARRAY(ShenandoahHeapRegion*, ShenandoahHeap::heap()->num_regions(), mtGC)),
_old_heuristics(nullptr),
_region_balance(0),

View File

@ -88,7 +88,7 @@ private:
bool coalesce_and_fill();
public:
ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity);
ShenandoahOldGeneration(uint max_queues, size_t max_capacity);
ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override;

View File

@ -30,8 +30,8 @@
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "gc/shenandoah/shenandoahYoungGeneration.hpp"
ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity) :
ShenandoahGeneration(YOUNG, max_queues, max_capacity, soft_max_capacity),
ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity) :
ShenandoahGeneration(YOUNG, max_queues, max_capacity),
_old_gen_task_queues(nullptr) {
}

View File

@ -34,7 +34,7 @@ private:
ShenandoahYoungHeuristics* _young_heuristics;
public:
ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t max_soft_capacity);
ShenandoahYoungGeneration(uint max_queues, size_t max_capacity);
ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override;

View File

@ -52,7 +52,7 @@ protected:
ShenandoahHeap::heap()->lock()->lock(false);
old = new ShenandoahOldGeneration(8, 1024 * 1024, 1024);
old = new ShenandoahOldGeneration(8, 1024 * 1024);
old->set_promoted_reserve(512 * HeapWordSize);
old->expend_promoted(256 * HeapWordSize);
old->set_evacuation_reserve(512 * HeapWordSize);

View File

@ -23,7 +23,63 @@
*
*/
/*
/**
* @test id=satb-adaptive
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000
* -XX:ShenandoahGCMode=satb
* -XX:+ShenandoahDegeneratedGC
* -XX:ShenandoahGCHeuristics=adaptive
* TestDynamicSoftMaxHeapSize
*
*/
/**
* @test id=satb-aggressive
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000
* -XX:ShenandoahGCMode=satb
* -XX:+ShenandoahDegeneratedGC
* -XX:ShenandoahGCHeuristics=aggressive
* TestDynamicSoftMaxHeapSize
*
*/
/**
* @test id=satb-compact
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000
* -XX:ShenandoahGCMode=satb
* -XX:+ShenandoahDegeneratedGC
* -XX:ShenandoahGCHeuristics=compact
* TestDynamicSoftMaxHeapSize
*
*/
/**
* @test id=satb-static
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000
* -XX:ShenandoahGCMode=satb
* -XX:+ShenandoahDegeneratedGC
* -XX:ShenandoahGCHeuristics=static
* TestDynamicSoftMaxHeapSize
*
*/
/**
* @test id=passive
* @requires vm.gc.Shenandoah
* @library /test/lib
@ -41,87 +97,101 @@
* TestDynamicSoftMaxHeapSize
*/
/*
* @test id=aggressive
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -Dtarget=1000
* TestDynamicSoftMaxHeapSize
*/
/*
* @test id=adaptive
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
* -Dtarget=10000
* TestDynamicSoftMaxHeapSize
*/
/*
/**
* @test id=generational
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* -Dtarget=10000
* @run main/othervm -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000
* -XX:ShenandoahGCMode=generational
* -XX:ShenandoahGCHeuristics=adaptive
* TestDynamicSoftMaxHeapSize
*
*/
/*
* @test id=static
/**
* @test id=generational-softMaxHeapSizeValidation
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=static
* -Dtarget=10000
* @run main/othervm -DvalidateSoftMaxHeap=true
* TestDynamicSoftMaxHeapSize
* -Xms100m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -Xlog:gc=info -Dtarget=10000 -DverifySoftMaxHeapValue=true
* -XX:ShenandoahGCMode=generational
* -XX:ShenandoahGCHeuristics=adaptive
*/
/*
* @test id=compact
* @requires vm.gc.Shenandoah
* @library /test/lib
*
* @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact
* -Dtarget=1000
* TestDynamicSoftMaxHeapSize
*/
import java.util.Random;
import jdk.test.lib.Utils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.dcmd.PidJcmdExecutor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class TestDynamicSoftMaxHeapSize {
static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation
static final long STRIDE = 10_000_000;
static volatile Object sink;
static final int K = 1024;
static final int XMS_MB = 100;
static final int XMX_MB = 512;
public static void main(String[] args) throws Exception {
long count = TARGET_MB * 1024 * 1024 / 16;
Random r = Utils.getRandomInstance();
PidJcmdExecutor jcmd = new PidJcmdExecutor();
if ("true".equals(System.getProperty("validateSoftMaxHeap"))) {
List<String> flagArgs = new ArrayList<>(Arrays.asList(args));
for (long c = 0; c < count; c += STRIDE) {
// Sizes specifically include heaps below Xms and above Xmx to test saturation code.
jcmd.execute("VM.set_flag SoftMaxHeapSize " + r.nextInt(768*1024*1024), true);
for (long s = 0; s < STRIDE; s++) {
sink = new Object();
}
Thread.sleep(1);
int softMaxInMb = Utils.getRandomInstance().nextInt(XMS_MB, XMX_MB);
flagArgs.add("-DsoftMaxCapacity=" + softMaxInMb * K * K);
flagArgs.add("-Dtest.jdk=" + System.getProperty("test.jdk"));
flagArgs.add("-Dcompile.jdk=" + System.getProperty("compile.jdk"));
flagArgs.add(SoftMaxWithExpectationTest.class.getName());
ProcessBuilder genShenPbValidateFlag = ProcessTools.createLimitedTestJavaProcessBuilder(flagArgs);
OutputAnalyzer output = new OutputAnalyzer(genShenPbValidateFlag.start());
output.shouldHaveExitValue(0);
output.shouldContain(String.format("Soft Max Heap Size: %dM -> %dM", XMX_MB, softMaxInMb)); // By default, the soft max heap size is Xmx
} else {
SoftMaxSetFlagOnlyTest.test();
}
}
public static class SoftMaxSetFlagOnlyTest {
static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation
static final long STRIDE = 10_000_000;
static volatile Object sink;
public static void test() throws Exception {
long count = TARGET_MB * 1024 * 1024 / 16;
Random r = Utils.getRandomInstance();
PidJcmdExecutor jcmd = new PidJcmdExecutor();
for (long c = 0; c < count; c += STRIDE) {
// Sizes specifically include heaps below Xms and above Xmx to test saturation code.
jcmd.execute("VM.set_flag SoftMaxHeapSize " + r.nextInt(768*1024*1024), true);
for (long s = 0; s < STRIDE; s++) {
sink = new Object();
}
Thread.sleep(1);
}
}
}
public static class SoftMaxWithExpectationTest {
static final long TOTAL = 100_000_000;
static volatile Object sink;
public static void main(String[] args) throws Exception {
int expectedSoftMaxHeapSize = Integer.getInteger("softMaxCapacity", 0);
PidJcmdExecutor jcmd = new PidJcmdExecutor();
jcmd.execute("VM.set_flag SoftMaxHeapSize " + expectedSoftMaxHeapSize, false);
for (long s = 0; s < TOTAL; s++) {
sink = new Object();
}
}
}
}