fix: Make old-gen promotion budget reservation lock-free

This commit is contained in:
Xiaolong Peng 2026-06-03 02:04:01 -07:00
parent 383cf6e9c2
commit 5a01d002bd
4 changed files with 22 additions and 12 deletions

View File

@ -1039,8 +1039,10 @@ HeapWord* ShenandoahHeap::allocate_memory_work(ShenandoahAllocRequest& req, bool
old_generation()->configure_plab_for_current_thread(req);
} else if (req.is_promotion()) {
const size_t actual_size = req.actual_size() * HeapWordSize;
log_debug(gc, plab)("Expend shared promotion of %zu bytes", actual_size);
old_generation()->expend_promoted(actual_size);
// Atomic reserve so concurrent GC workers can't over-expend the promotion budget without the lock.
if (!old_generation()->try_expend_promoted(actual_size)) {
log_debug(gc, plab)("Shared promotion of %zu bytes exceeded promotion reserve", actual_size);
}
}
}
}

View File

@ -193,9 +193,17 @@ void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent)
}
}
size_t ShenandoahOldGeneration::expend_promoted(size_t increment) {
assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted");
return _promoted_expended.add_then_fetch(increment);
bool ShenandoahOldGeneration::try_expend_promoted(size_t increment) {
const size_t reserve = get_promoted_reserve();
size_t cur = _promoted_expended.load_relaxed();
while (cur + increment <= reserve) {
size_t prev = _promoted_expended.compare_exchange(cur, cur + increment);
if (prev == cur) {
return true;
}
cur = prev;
}
return false;
}
size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) {
@ -244,12 +252,11 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc
// The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries).
// If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread.
if (can_promote(actual_size)) {
if (try_expend_promoted(actual_size)) {
// Assume the entirety of this PLAB will be used for promotion. This prevents promotion from overreach.
// When we retire this plab, we'll unexpend what we don't really use.
log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu",
actual_size, get_promoted_expended(), get_promoted_reserve());
expend_promoted(actual_size);
shenandoah_plab->enable_promotions();
shenandoah_plab->set_actual_size(actual_size);
} else {

View File

@ -111,8 +111,9 @@ public:
// This zeros out the expended promotion count after the promotion reserve is computed
void reset_promoted_expended();
// This is incremented when allocations are made to copy promotions into the old generation
size_t expend_promoted(size_t increment);
// Atomically reserve `increment` bytes of promotion budget. Returns true iff the full amount
// was reserved without exceeding the reserve. Lock-free: safe to call without the heap lock.
bool try_expend_promoted(size_t increment);
// This is used to return unused memory from a retired promotion LAB
size_t unexpend_promoted(size_t decrement);

View File

@ -55,7 +55,7 @@ protected:
old = new ShenandoahOldGeneration(8);
old->set_promoted_reserve(512 * HeapWordSize);
old->expend_promoted(256 * HeapWordSize);
old->try_expend_promoted(256 * HeapWordSize);
old->set_evacuation_reserve(512 * HeapWordSize);
Thread* thread = Thread::current();
@ -171,10 +171,10 @@ TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserv
EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations";
}
TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted_should_increase_expended) {
TEST_VM_F(ShenandoahOldGenerationTest, test_try_expend_promoted_should_increase_expended) {
SKIP_IF_NOT_SHENANDOAH();
size_t expended_before = old->get_promoted_expended();
old->expend_promoted(128);
EXPECT_TRUE(old->try_expend_promoted(128)) << "Should fit within reserve";
size_t expended_after = old->get_promoted_expended();
EXPECT_EQ(expended_before + 128, expended_after) << "Should expend promotion";
}