[vm] Account for external promotions; include external allocations in idle GC trigger.
A new-space collection during AllocateExternal may promote enough external allocations to warrant an old-space collection.
Bug: https://github.com/dart-lang/sdk/issues/33314
Change-Id: Ie7855e506869535435dc76b61ad30e5f42eda4a2
Reviewed-on: https://dart-review.googlesource.com/63449
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 376f9b7..9a281b0 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -135,16 +135,17 @@
// Attempt to free some external allocation by a scavenge. (If the total
// remains above the limit, next external alloc will trigger another.)
CollectGarbage(kScavenge, kExternal);
+ // Promotion may have pushed old space over its limit.
+ if (old_space_.NeedsGarbageCollection()) {
+ CollectGarbage(kMarkSweep, kExternal);
+ }
}
} else {
ASSERT(space == kOld);
old_space_.AllocateExternal(cid, size);
- }
- // Idle GC does not check whether promotions should trigger a full GC.
- // As a workaround, we check here on every external allocation. See issue
- // dartbug.com/33314.
- if (old_space_.NeedsGarbageCollection()) {
- CollectAllGarbage(kExternal);
+ if (old_space_.NeedsGarbageCollection()) {
+ CollectAllGarbage(kExternal);
+ }
}
}
diff --git a/runtime/vm/heap/heap_test.cc b/runtime/vm/heap/heap_test.cc
index 1281e13..e2f2379 100644
--- a/runtime/vm/heap/heap_test.cc
+++ b/runtime/vm/heap/heap_test.cc
@@ -576,4 +576,39 @@
EXPECT(size_before < size_after);
}
+static void NoopFinalizer(void* isolate_callback_data,
+ Dart_WeakPersistentHandle handle,
+ void* peer) {}
+
+ISOLATE_UNIT_TEST_CASE(ExternalPromotion) {
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+
+ heap->CollectAllGarbage();
+ intptr_t size_before = kWordSize * (heap->new_space()->ExternalInWords() +
+ heap->old_space()->ExternalInWords());
+
+ Array& old = Array::Handle(Array::New(100, Heap::kOld));
+ Array& neu = Array::Handle();
+ for (intptr_t i = 0; i < 100; i++) {
+ neu = Array::New(1, Heap::kNew);
+ FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB);
+ old.SetAt(i, neu);
+ }
+
+ intptr_t size_middle = kWordSize * (heap->new_space()->ExternalInWords() +
+ heap->old_space()->ExternalInWords());
+ EXPECT_EQ(size_before + 100 * MB, size_middle);
+
+ old = Array::null();
+ neu = Array::null();
+
+ heap->CollectAllGarbage();
+
+ intptr_t size_after = kWordSize * (heap->new_space()->ExternalInWords() +
+ heap->old_space()->ExternalInWords());
+
+ EXPECT_EQ(size_before, size_after);
+}
+
} // namespace dart
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 344932e..fe335e4 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1268,7 +1268,8 @@
if (heap_growth_ratio_ == 100) {
return false;
}
- return current.capacity_in_words > idle_gc_threshold_in_words_;
+ return (current.capacity_in_words > idle_gc_threshold_in_words_) ||
+ NeedsExternalCollection(current);
}
void PageSpaceController::EvaluateGarbageCollection(SpaceUsage before,