@@ -931,7 +931,9 @@ HeapWord* ShenandoahFreeSet::try_allocate_from_mutator(ShenandoahAllocRequest& r
931
931
ShenandoahHeapRegion* r = _heap->get_region (idx);
932
932
if (can_allocate_from (r)) {
933
933
if (req.is_old ()) {
934
- flip_to_old_gc (r);
934
+ if (!flip_to_old_gc (r)) {
935
+ continue ;
936
+ }
935
937
} else {
936
938
flip_to_gc (r);
937
939
}
@@ -1284,25 +1286,65 @@ void ShenandoahFreeSet::recycle_trash() {
1284
1286
heap->parallel_heap_region_iterate (&closure);
1285
1287
}
1286
1288
1287
- void ShenandoahFreeSet::flip_to_old_gc (ShenandoahHeapRegion* r) {
1288
- size_t idx = r->index ();
1289
+ bool ShenandoahFreeSet::flip_to_old_gc (ShenandoahHeapRegion* r) {
1290
+ const size_t idx = r->index ();
1289
1291
1290
1292
assert (_partitions.partition_id_matches (idx, ShenandoahFreeSetPartitionId::Mutator), " Should be in mutator view" );
1291
1293
assert (can_allocate_from (r), " Should not be allocated" );
1292
1294
1293
1295
ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap ();
1294
- size_t region_capacity = alloc_capacity (r);
1295
- _partitions.move_from_partition_to_partition (idx, ShenandoahFreeSetPartitionId::Mutator,
1296
- ShenandoahFreeSetPartitionId::OldCollector, region_capacity);
1297
- _partitions.assert_bounds ();
1298
- _heap->old_generation ()->augment_evacuation_reserve (region_capacity);
1296
+ const size_t region_capacity = alloc_capacity (r);
1297
+
1299
1298
bool transferred = gen_heap->generation_sizer ()->transfer_to_old (1 );
1300
- if (!transferred) {
1301
- log_warning (gc, free)(" Forcing transfer of %zu to old reserve." , idx);
1302
- gen_heap->generation_sizer ()->force_transfer_to_old (1 );
1299
+ if (transferred) {
1300
+ _partitions.move_from_partition_to_partition (idx, ShenandoahFreeSetPartitionId::Mutator,
1301
+ ShenandoahFreeSetPartitionId::OldCollector, region_capacity);
1302
+ _partitions.assert_bounds ();
1303
+ _heap->old_generation ()->augment_evacuation_reserve (region_capacity);
1304
+ return true ;
1303
1305
}
1304
- // We do not ensure that the region is no longer trash, relying on try_allocate_in(), which always comes next,
1305
- // to recycle trash before attempting to allocate anything in the region.
1306
+
1307
+ if (_heap->young_generation ()->free_unaffiliated_regions () == 0 && _heap->old_generation ()->free_unaffiliated_regions () > 0 ) {
1308
+ // Old has free unaffiliated regions, but it couldn't use them for allocation (likely because they
1309
+ // are trash and weak roots are in process). In this scenario, we aren't really stealing from the
1310
+ // mutator (they have nothing to steal), but they do have a usable region in their partition. What
1311
+ // we want to do here is swap that region from the mutator partition with one from the old collector
1312
+ // partition.
1313
+ // 1. Find a temporarily unusable trash region in the old collector partition
1314
+ ShenandoahRightLeftIterator iterator (&_partitions, ShenandoahFreeSetPartitionId::OldCollector, true );
1315
+ idx_t unusable_trash = -1 ;
1316
+ for (unusable_trash = iterator.current (); iterator.has_next (); unusable_trash = iterator.next ()) {
1317
+ const ShenandoahHeapRegion* region = _heap->get_region (unusable_trash);
1318
+ if (region->is_trash () && _heap->is_concurrent_weak_root_in_progress ()) {
1319
+ break ;
1320
+ }
1321
+ }
1322
+
1323
+ if (unusable_trash != -1 ) {
1324
+ const size_t unusable_capacity = alloc_capacity (unusable_trash);
1325
+ // 2. Move the (temporarily) unusable trash region we found to the mutator partition
1326
+ _partitions.move_from_partition_to_partition (unusable_trash,
1327
+ ShenandoahFreeSetPartitionId::OldCollector,
1328
+ ShenandoahFreeSetPartitionId::Mutator, unusable_capacity);
1329
+
1330
+ // 3. Move this usable region from the mutator partition to the old collector partition
1331
+ _partitions.move_from_partition_to_partition (idx,
1332
+ ShenandoahFreeSetPartitionId::Mutator,
1333
+ ShenandoahFreeSetPartitionId::OldCollector, region_capacity);
1334
+
1335
+ _partitions.assert_bounds ();
1336
+
1337
+ // 4. Do not adjust capacities for generations, we just swapped the regions that have already
1338
+ // been accounted for. However, we should adjust the evacuation reserves as those may have changed.
1339
+ shenandoah_assert_heaplocked ();
1340
+ const size_t reserve = _heap->old_generation ()->get_evacuation_reserve ();
1341
+ _heap->old_generation ()->set_evacuation_reserve (reserve - unusable_capacity + region_capacity);
1342
+ return true ;
1343
+ }
1344
+ }
1345
+
1346
+ // We can't take this region young because it has no free unaffiliated regions (transfer failed).
1347
+ return false ;
1306
1348
}
1307
1349
1308
1350
void ShenandoahFreeSet::flip_to_gc (ShenandoahHeapRegion* r) {
0 commit comments