8386287: GenShen: assert(starts_object(card_index)) failed: Can't get last start because no object starts here

Reviewed-by: kdnilsen
Backport-of: 1251526588314059c6c0bc132c1a052ef04c83a6
This commit is contained in:
William Kemper 2026-06-18 16:50:44 +00:00
parent 10bee9ceca
commit 8f260e6f9b
5 changed files with 21 additions and 14 deletions

View File

@ -144,7 +144,7 @@ void ShenandoahGenerationalFullGC::account_for_region(ShenandoahHeapRegion* r, s
void ShenandoahGenerationalFullGC::maybe_coalesce_and_fill_region(ShenandoahHeapRegion* r) {
if (r->is_pinned() && r->is_old() && r->is_active() && !r->is_humongous()) {
r->begin_preemptible_coalesce_and_fill();
r->oop_coalesce_and_fill(false);
r->oop_coalesce_and_fill(/* cancellable = */ false, /* do_card_table_updates = */ false);
}
}

View File

@ -452,7 +452,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const {
}
// oop_iterate without closure, return true if completed without cancellation
bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) {
bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable, bool do_card_table_updates) {
assert(!is_humongous(), "No need to fill or coalesce humongous regions");
if (!is_active()) {
@ -489,7 +489,16 @@ bool ShenandoahHeapRegion::oop_coalesce_and_fill(bool cancellable) {
size_t fill_size = next_marked_obj - obj_addr;
assert(fill_size >= ShenandoahHeap::min_fill_size(), "previously allocated object known to be larger than min_size");
ShenandoahHeap::fill_with_object(obj_addr, fill_size);
heap->old_generation()->card_scan()->coalesce_objects(obj_addr, fill_size);
if (do_card_table_updates) {
heap->old_generation()->card_scan()->coalesce_objects(obj_addr, fill_size);
} else {
// A humongous object allocation failure during evacuation will skip the degenerated cycle and
// jump straight to a full GC. If this region is pinned when the full GC cycle starts, it will
// not be compacted. Therefore, if the region is old, we must fill in any unmarked objects. However,
// promoted objects will not have been registered yet, so we cannot use the card table here.
assert(heap->is_full_gc_in_progress(), "Can only skip card table updates during a full GC");
}
obj_addr = next_marked_obj;
}
if (cancellable && heap->cancelled_gc()) {

View File

@ -447,7 +447,7 @@ public:
// This is used by old-gen GC following concurrent marking to make old-gen HeapRegions parsable. Old regions must be
// parsable because the mark bitmap is not reliable during the concurrent old mark.
// Return true iff region is completely coalesced and filled. Returns false if cancelled before task is complete.
bool oop_coalesce_and_fill(bool cancellable);
bool oop_coalesce_and_fill(bool cancellable, bool do_card_table_updates = true);
// Invoke closure on every reference contained within the humongous object that spans this humongous
// region if the reference is contained within a DIRTY card and the reference is no more than words following

View File

@ -28,8 +28,6 @@
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahPLAB.hpp"
#include "logging/log.hpp"
#include "runtime/globals.hpp"
#include "runtime/javaThread.hpp"
#include "utilities/copy.hpp"
ShenandoahPLAB::ShenandoahPLAB() :
@ -46,9 +44,7 @@ ShenandoahPLAB::ShenandoahPLAB() :
}
ShenandoahPLAB::~ShenandoahPLAB() {
if (_plab != nullptr) {
delete _plab;
}
delete _plab;
}
void ShenandoahPLAB::subtract_from_promoted(size_t increment) {
@ -119,7 +115,7 @@ HeapWord* ShenandoahPLAB::allocate_slow(size_t size, bool is_promotion) {
}
if (_plab->words_remaining() < plab_min_size) {
// Retire current PLAB. This takes care of any PLAB book-keeping.
// Retire current PLAB. This takes care of any PLAB bookkeeping.
retire();
size_t actual_size = 0;
@ -191,10 +187,12 @@ void ShenandoahPLAB::retire() {
log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize);
_heap->old_generation()->unexpend_promoted(not_promoted);
}
const size_t original_waste = _plab->waste();
HeapWord* const top = _plab->top();
// plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable.
// It adds the size of this unused memory, in words, to plab->waste().
// We do _not_ need to register this remnant object with the card table because all paths where a PLAB object would
// be created are covered by a subsequent phase in the cycle. For the concurrent and degenerated cycles, all PLABs
// are retired in preparation for update-references. All objects in these PLABs will be registered by update-card-tables.
// For a full GC, the entire remembered set will be rebuilt in the final phase. Note also that an empty TLAB will _not_
// create a filler object when it is retired.
_plab->retire();
}

View File

@ -388,7 +388,7 @@ public:
_used(0), _committed(0), _garbage(0), _regions(0), _humongous_waste(0), _trashed_regions(0), _trashed_used(0)
{
_region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
// Retired regions are not necessarily filled, thouugh their remnant memory is considered used.
// Retired regions are not necessarily filled, though their remnant memory is considered used.
_min_free_size = PLAB::min_size() * HeapWordSize;
};