8386512: Shenandoah: Add a diagnostic option to facilitate testing pinned regions

Reviewed-by: kdnilsen, wkemper
This commit is contained in:
Patrick Fontanilla 2026-07-07 20:23:42 +00:00 committed by Kelvin Nilsen
parent 74f9b51f34
commit 5926bbfa0a
17 changed files with 168 additions and 1 deletions

View File

@ -109,6 +109,7 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare);
EventMark em("%s", msg);
heap->try_inject_pin();
// Evacuation is complete, retire gc labs and change gc state
heap->concurrent_prepare_for_update_refs();
}
@ -125,6 +126,7 @@ void ShenandoahConcurrentGC::entry_update_card_table() {
ShenandoahWorkerPolicy::calc_workers_for_conc_evac(),
"concurrent update cards");
heap->try_inject_pin();
// Heap needs to be parsable here.
// Also, parallel heap region iterate must have a phase set.
assert(ShenandoahTimingsTracker::is_current_phase_valid(), "Current phase must be set");
@ -301,6 +303,7 @@ void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() {
ShenandoahWorkerPolicy::calc_workers_for_conc_evac(),
msg);
heap->try_inject_pin();
// We chose not to evacuate because we found sufficient immediate garbage.
// However, there may still be regions to promote in place, so do that now.
if (heap->old_generation()->has_in_place_promotions()) {
@ -335,6 +338,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_mark() {
heap->try_inject_alloc_failure();
VM_ShenandoahFinalMarkStartEvac op(this);
VMThread::execute(&op); // jump to entry_final_mark under safepoint
heap->try_inject_pin();
}
void ShenandoahConcurrentGC::vmop_entry_init_update_refs() {
@ -343,6 +347,7 @@ void ShenandoahConcurrentGC::vmop_entry_init_update_refs() {
ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::init_update_refs_gross);
heap->try_inject_alloc_failure();
heap->try_inject_pin();
VM_ShenandoahInitUpdateRefs op(this);
VMThread::execute(&op);
}
@ -353,6 +358,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_update_refs() {
ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_update_refs_gross);
heap->try_inject_alloc_failure();
heap->try_inject_pin();
VM_ShenandoahFinalUpdateRefs op(this);
VMThread::execute(&op);
}
@ -364,6 +370,7 @@ void ShenandoahConcurrentGC::vmop_entry_final_verify() {
// This phase does not use workers, no need for setup
heap->try_inject_alloc_failure();
heap->try_inject_pin();
VM_ShenandoahFinalVerify op(this);
VMThread::execute(&op);
}
@ -423,6 +430,7 @@ void ShenandoahConcurrentGC::entry_final_verify() {
void ShenandoahConcurrentGC::entry_reset() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
heap->release_injected_pins();
heap->try_inject_alloc_failure();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
@ -483,6 +491,7 @@ void ShenandoahConcurrentGC::entry_mark() {
heap->try_inject_alloc_failure();
op_mark();
heap->try_inject_pin();
}
void ShenandoahConcurrentGC::entry_thread_roots() {
@ -496,6 +505,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() {
msg);
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_thread_roots();
}
@ -510,6 +520,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() {
"concurrent weak references");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_weak_refs();
}
@ -525,6 +536,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() {
"concurrent weak root");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_weak_roots();
}
@ -540,6 +552,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() {
"concurrent class unloading");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_class_unloading();
}
@ -557,6 +570,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() {
"concurrent strong root");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_strong_roots();
}
@ -569,6 +583,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() {
// This phase does not use workers, no need for setup
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_cleanup_early();
if (!heap->is_evacuation_in_progress()) {
// This is an abbreviated cycle. Rebuild the freeset in order to establish reserves for the next GC cycle. Doing
@ -591,6 +606,7 @@ void ShenandoahConcurrentGC::entry_evacuate() {
"concurrent evacuation");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_evacuate();
}
@ -604,6 +620,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() {
// No workers used in this phase, no setup required
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_update_thread_roots();
}
@ -619,6 +636,7 @@ void ShenandoahConcurrentGC::entry_update_refs() {
"concurrent reference update");
heap->try_inject_alloc_failure();
heap->try_inject_pin();
op_update_refs();
}

View File

@ -91,6 +91,7 @@ void ShenandoahDegenGC::entry_degenerated() {
void ShenandoahDegenGC::op_degenerated() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
heap->release_injected_pins();
// Degenerated GC is STW, but it can also fail. Current mechanics communicates
// GC failure via cancelled_concgc() flag. So, if we detect the failure after
// some phase, we have to upgrade the Degenerate GC to Full GC.

View File

@ -136,6 +136,7 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) {
void ShenandoahFullGC::do_it(GCCause::Cause gc_cause) {
ShenandoahHeap* heap = ShenandoahHeap::heap();
heap->release_injected_pins();
// A full GC may be entered directly, or as an upgrade from a failed
// degenerated GC. In the latter case, self-forwarded objects may be

View File

@ -595,7 +595,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) :
_aux_bitmap_region_special(false),
_liveness_cache(nullptr),
_collection_set(nullptr),
_evac_tracker(new ShenandoahEvacuationTracker())
_evac_tracker(new ShenandoahEvacuationTracker()),
_injected_pin_count(0)
{
// Initialize GC mode early, many subsequent initialization procedures depend on it
initialize_mode();
@ -2752,6 +2753,39 @@ bool ShenandoahHeap::should_inject_alloc_failure() {
return _inject_alloc_failure.is_set() && _inject_alloc_failure.try_unset();
}
void ShenandoahHeap::try_inject_pin() {
assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "try_inject_pin() must be called outside a safepoint.");
assert(active_generation() != nullptr, "Active generation must be set before we inject pins.");
assert(is_concurrent_mark_in_progress() || active_generation()->is_mark_complete(),
"try_inject_pin() requires marking is in progress or has completed.");
if (ShenandoahPinRegionRate && !cancelled_gc() && ((uintx)(os::random() % 1000) < ShenandoahPinRegionRate) &&
_injected_pin_count < MAX_INJECTED_PINS) {
const size_t idx = os::random() % num_regions();
ShenandoahHeapRegion* r = get_region(idx);
if ((r->is_regular() || r->is_humongous_start()) && r->has_live()) {
r->record_pin();
_injected_pin_indices[_injected_pin_count] = idx;
_injected_pin_count++;
}
}
}
void ShenandoahHeap::release_injected_pins() {
if (_injected_pin_count == 0) {
return;
}
assert(_injected_pin_count <= MAX_INJECTED_PINS,
"Injected pin count: %u exceeds max: %u.", _injected_pin_count, MAX_INJECTED_PINS);
for (uint i = 0; i < _injected_pin_count; i++) {
const size_t idx = _injected_pin_indices[i];
ShenandoahHeapRegion* r = get_region(idx);
assert(r->pin_count() > 0, "Region %zu in tracker must contain a pin.", idx);
r->record_unpin();
}
_injected_pin_count = 0;
}
void ShenandoahHeap::initialize_serviceability() {
_memory_pool = new ShenandoahMemoryPool(this);
_cycle_memory_manager.add_pool(_memory_pool);

View File

@ -868,6 +868,19 @@ private:
void try_inject_alloc_failure();
bool should_inject_alloc_failure();
// Randomly pin a region when ShenandoahPinRegionRate > 0. Pin injection is only called after
// the cycle has populated _live_data and runs concurrently on the control thread. Releasing
// injected pins is done at the start of every cycle preventing stale pinned region states.
void try_inject_pin();
void release_injected_pins();
// Maximum number of regions that can be injected with pins.
static const uint MAX_INJECTED_PINS = 32;
// Tracker for injected pins added by try_inject_pin().
size_t _injected_pin_indices[MAX_INJECTED_PINS];
uint _injected_pin_count;
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_HPP

View File

@ -84,6 +84,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) {
auto heap = ShenandoahGenerationalHeap::heap();
assert(!_old_generation->is_doing_mixed_evacuations(), "Should not start an old gc with pending mixed evacuations");
assert(!_old_generation->is_preparing_for_mark(), "Old regions need to be parsable during concurrent mark.");
heap->release_injected_pins();
// Enable preemption of old generation mark.
_allow_preemption.set();

View File

@ -461,6 +461,12 @@
product(bool, ShenandoahAllocFailureALot, false, DIAGNOSTIC, \
"Testing: make lots of artificial allocation failures.") \
\
product(uintx, ShenandoahPinRegionRate, 0, DIAGNOSTIC, \
"Testing: rate at which to artificially pin regions. Expressed " \
"as N in 1000 chances for a region to be randomly pinned per " \
"injection attempt.") \
range(0, 1000) \
\
product(uintx, ShenandoahCoalesceChance, 0, DIAGNOSTIC, \
"Testing: Abandon remaining mixed collections with this " \
"likelihood. Following each mixed collection, abandon all " \

View File

@ -71,6 +71,11 @@
*
* @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestAllocHumongousFragment
*
* @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
* TestAllocHumongousFragment
*
@ -78,6 +83,11 @@
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahAllocFailureALot
* TestAllocHumongousFragment
*
* @run main/othervm -Xmx1g -Xms1g -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahTargetNumRegions=2048
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestAllocHumongousFragment
*/
/*

View File

@ -70,6 +70,11 @@
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestAllocIntArrays
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
* TestAllocIntArrays
*
@ -80,6 +85,11 @@
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestAllocIntArrays
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestAllocIntArrays
*/

View File

@ -70,6 +70,11 @@
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestAllocObjectArrays
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
* TestAllocObjectArrays
*
@ -80,6 +85,11 @@
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestAllocObjectArrays
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestAllocObjectArrays
*/
@ -126,6 +136,12 @@
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* -XX:ShenandoahPinRegionRate=1000
* -XX:+ShenandoahVerify
* TestAllocObjectArrays
*
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* TestAllocObjectArrays
*/

View File

@ -64,6 +64,11 @@
* -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify
* TestAllocObjects
*
* @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestAllocObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
@ -76,6 +81,11 @@
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestAllocObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestAllocObjects
*/

View File

@ -58,6 +58,11 @@
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestJcmdHeapDump
*/

View File

@ -58,6 +58,12 @@
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* -Dtarget=1000
* TestLotsOfCycles
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -Dtarget=1000
* TestLotsOfCycles
*/

View File

@ -66,6 +66,11 @@
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestRetainObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestRetainObjects
*/
@ -106,6 +111,11 @@
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestRetainObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* TestRetainObjects
*/

View File

@ -68,6 +68,11 @@
* -XX:+ShenandoahAllocFailureALot
* TestSieveObjects
*
* @run main/othervm/timeout=240 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* TestSieveObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestSieveObjects
@ -108,6 +113,11 @@
* -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify
* TestSieveObjects
*
* @run main/othervm/timeout=480 -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* -XX:ShenandoahPinRegionRate=1000 -XX:+ShenandoahVerify
* TestSieveObjects
*
* @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
* TestSieveObjects

View File

@ -66,6 +66,11 @@ import java.io.IOException;
*
* @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* gc.stress.gcbasher.TestGCBasherWithShenandoah 120000
*
* @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* gc.stress.gcbasher.TestGCBasherWithShenandoah 120000
*/
@ -144,6 +149,12 @@ import java.io.IOException;
* @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline
* -XX:ShenandoahPinRegionRate=1000
* gc.stress.gcbasher.TestGCBasherWithShenandoah 120000
*
* @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline
* gc.stress.gcbasher.TestGCBasherWithShenandoah 120000
*/

View File

@ -71,6 +71,11 @@ package gc.stress.gcold;
*
* @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:ShenandoahPinRegionRate=1000
* gc.stress.gcold.TestGCOld 50 1 20 10 10000
*
* @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* gc.stress.gcold.TestGCOld 50 1 20 10 10000
*/