RFR: 8377048: Shenandoah: shenandoahLock related improvments [v6]
Xiaolong Peng
xpeng at openjdk.org
Tue Feb 24 17:58:44 UTC 2026
On Wed, 4 Feb 2026 07:40:36 GMT, Xiaolong Peng <xpeng at openjdk.org> wrote:
>> src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp line 790:
>>
>>> 788: // inconsistent data: available may not equal capacity - used because the intermediate states of any "atomic"
>>> 789: // locked action can be seen by these unlocked functions.
>>> 790: inline size_t capacity_holding_lock() const {
>>
>> It looks like you're adding overhead to these calls here. Previously, we were able to ask for capacity() and used() and available() without needing to pay the cost of acquiring a lock. Now, we always have to acquire the lock(). Have you measured impact?
>
> I didn't run test for it yet, but for non-debug build, the capacity/used/available are not frequently read, the overhead should have very minor impact(it may still compete with thread doing rebuild).
> In debug build, because of the asserts need to read the accountings much more often, it should have a small performance impact.
>
> The change is really about correctness, these filed may be be updated with rebuild lock being hold, but can be read w/o acquiring the rebuild lock, so a thread reading them w/o the rebuild lock may see stale value.
>
> As a part of the change of JDK-8373371, I am updating how the heap usage accountings are updated:
> 1. All changes to heap usage accountings must be done with holding heap lock and heap usage accounting lock(rebuild lock will be renamed to usage accounting lock),
> 2. All reads must be done under heap usage accounting lock, no need to hold heap lock.
> 3. Computation of total/young/old accountings will be done in the read path under heap usage accounting lock.
>
> Here are the examples of young_used(), old_used(), global_used() with deferred re-computation:
>
> // Return bytes used by young
> inline size_t young_used() {
> ShenandoahHeapUsageAccountingLocker locker(usage_accounting_lock());
> return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator) +
> _partitions.used_by(ShenandoahFreeSetPartitionId::Collector);
> }
>
> // Return bytes used by old
> inline size_t old_used() {
> ShenandoahHeapUsageAccountingLocker locker(usage_accounting_lock());
> return _partitions.used_by(ShenandoahFreeSetPartitionId::OldCollector);
> }
>
> // Return bytes used by global
> inline size_t global_used() {
> ShenandoahHeapUsageAccountingLocker locker(usage_accounting_lock());
> return young_used() + old_used();
> }
>
>
> ShenandoahHeapUsageAccountingLock is a reentrant lock, global_used acquires the lock first, so it will have stable snapshot of young_used and old_used when it calculate the global_used.
>
> Reentrance of the ShenandoahHeapUsageAccountingLock is very cheap since the current thread already holds it, it just checks the owner of the lock and increase the counter.
>
> I have pushed this change to gitfarm pipeline, I'm not really expecting any performance regression form this, I'll check the result tomorrow.
I have reverted this change, I'll include it in the following PR deferring accounting re-computation.
>> src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp line 2848:
>>
>>> 2846: ShenandoahHeapLocker::ShenandoahHeapLocker(ShenandoahHeapLock* lock, bool allow_block_for_safepoint) : _lock(lock) {
>>> 2847: ShenandoahFreeSet* free_set = ShenandoahHeap::heap()->free_set();
>>> 2848: assert(free_set == nullptr || !free_set->rebuild_lock()->owned_by_self(), "Dead lock, can't acquire heap lock while holding free-set rebuild lock");
>>
>> Can we add a comment to explain when free_set will equal nullptr? Does this correspond to "pre-initialized state" or something else?
>>
>> Would probably be more clear that we don't need to consult free_set unless asserts are enabled, so enclose this code in #ifdef ASSERT
>
> Yes, it is related to initialization, and only happens in the "pre-initialized state", I'll add some comments for this.
I have updated the comments and added #ifdef ASSERT to enclose the asserts
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/29543#discussion_r2848719927
PR Review Comment: https://git.openjdk.org/jdk/pull/29543#discussion_r2848722200
More information about the shenandoah-dev
mailing list