From b76efb098b2b8b3a73363623e15249051870dccc Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Mon, 16 Mar 2026 10:24:42 -0700 Subject: [PATCH] [vm, gc] Also apply back pressure during concurrent sweep. When running without concurrent marking, the growth policy sets an allocation limit that triggers a blocking GC. When running with concurrent marking, the growth policy sets an allocation limit that triggers the start of concurrent marking, but the mutator will continue to allocate until marking completes. By itself, this would allow the heap to grow far beyond its working set size, so back pressure is applied to the mutator by making it do some incremental marking work in proportion to the amount it allocates. A similar race between mutator and GC exists for concurrent sweep, so make the mutator do some incremental sweeping too. Cf. 305c3e30c6aea9c05a1abec35eb5f7e09f716348. TEST=golem Bug: https://github.com/dart-lang/sdk/issues/62762 Change-Id: I6f882e6893e1cd3b3591db8cd03b61eb06bfa02f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/486828 Reviewed-by: Slava Egorov Commit-Queue: Ryan Macnak --- runtime/vm/heap/heap.cc | 9 +++++++-- runtime/vm/heap/pages.cc | 11 ++++++++++- runtime/vm/heap/pages.h | 3 ++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index 42cd82c03d9..32053fcaefd 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc @@ -616,12 +616,17 @@ void Heap::CheckConcurrentMarking(Thread* thread, switch (phase) { case PageSpace::kMarking: if (mode_ != Dart_PerformanceMode_Latency) { - old_space_.IncrementalMarkWithSizeBudget(size); + // Back pressure: do slightly more marking work than allocation work. + old_space_.IncrementalMarkWithSizeBudget(size + (size >> 2)); } return; case PageSpace::kSweepingLarge: case PageSpace::kSweepingRegular: - return; // Busy. + if (mode_ != Dart_PerformanceMode_Latency) { + // Back pressure: do some sweeping work. + old_space_.IncrementalSweepWithSizeBudget(size); + } + return; case PageSpace::kAwaitingFinalization: CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kFinalize); return; diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc index dedd1671dfd..40ad4f965c4 100644 --- a/runtime/vm/heap/pages.cc +++ b/runtime/vm/heap/pages.cc @@ -899,6 +899,13 @@ void PageSpace::IncrementalMarkWithTimeBudget(int64_t deadline) { } } +void PageSpace::IncrementalSweepWithSizeBudget(intptr_t size) { + if (size >= kAllocatablePageSize) { + // Sweeping work is less divisible than marking work. + Sweep(/*exclusive=*/false, /*one_page=*/true); + } +} + void PageSpace::AssistTasks(MonitorLocker* ml) { if (phase() == PageSpace::kMarking) { ml->Exit(); @@ -1390,7 +1397,7 @@ void PageSpace::SweepLarge() { } } -void PageSpace::Sweep(bool exclusive) { +void PageSpace::Sweep(bool exclusive, bool one_page) { TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "Sweep"); GCSweeper sweeper; @@ -1436,6 +1443,8 @@ void PageSpace::Sweep(bool exclusive) { } else { IncreaseCapacityInWordsLocked(-(size >> kWordSizeLog2)); } + + if (one_page) break; } if (exclusive) { diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h index 970a4c9c2fe..e18f8adbc62 100644 --- a/runtime/vm/heap/pages.h +++ b/runtime/vm/heap/pages.h @@ -259,6 +259,7 @@ class PageSpace { bool ShouldPerformIdleMarkCompact(int64_t deadline); void IncrementalMarkWithSizeBudget(intptr_t size); void IncrementalMarkWithTimeBudget(int64_t deadline); + void IncrementalSweepWithSizeBudget(intptr_t size); void AssistTasks(MonitorLocker* ml); void AddGCTime(int64_t micros) { gc_time_micros_ += micros; } @@ -426,7 +427,7 @@ class PageSpace { void SweepExecutable(); void SweepNew(); void SweepLarge(); - void Sweep(bool exclusive); + void Sweep(bool exclusive, bool one_page = false); void ConcurrentSweep(IsolateGroup* isolate_group); void Compact(Thread* thread);