[vm, gc] Refactor PageSpace::CollectGarbage to ensure the safepoint and task count remain well-scoped even with early exits.
Landing this separately makes it easier to see the changes for concurrent marking.
Bug: https://github.com/dart-lang/sdk/issues/34002
Change-Id: I8e2e2ca0cddbaba079cf54b6e8cff65c42e21684
Reviewed-on: https://dart-review.googlesource.com/68980
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 00186fa..253da16 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -901,8 +901,6 @@
void PageSpace::CollectGarbage(bool compact) {
Thread* thread = Thread::Current();
- Isolate* isolate = heap_->isolate();
- ASSERT(isolate == Isolate::Current());
const int64_t pre_wait_for_sweepers = OS::GetCurrentMonotonicMicros();
@@ -923,149 +921,7 @@
// loser skips collection and goes straight to allocation.
{
SafepointOperationScope safepoint_scope(thread);
-
- const int64_t start = OS::GetCurrentMonotonicMicros();
-
- NOT_IN_PRODUCT(isolate->class_table()->ResetCountersOld());
- // Perform various cleanup that relies on no tasks interfering.
- isolate->class_table()->FreeOldTables();
-
- NoSafepointScope no_safepoints;
-
- if (FLAG_print_free_list_before_gc) {
- OS::PrintErr("Data Freelist (before GC):\n");
- freelist_[HeapPage::kData].Print();
- OS::PrintErr("Executable Freelist (before GC):\n");
- freelist_[HeapPage::kExecutable].Print();
- }
-
- if (FLAG_verify_before_gc) {
- OS::PrintErr("Verifying before marking...");
- heap_->VerifyGC();
- OS::PrintErr(" done.\n");
- }
-
- // Make code pages writable.
- WriteProtectCode(false);
-
- // Save old value before GCMarker visits the weak persistent handles.
- SpaceUsage usage_before = GetCurrentUsage();
-
- // Mark all reachable old-gen objects.
-#if defined(PRODUCT)
- bool collect_code = FLAG_collect_code && ShouldCollectCode();
-#else
- bool collect_code = FLAG_collect_code && ShouldCollectCode() &&
- !isolate->HasAttemptedReload();
-#endif // !defined(PRODUCT)
- GCMarker marker(heap_);
- marker.MarkObjects(isolate, this, collect_code);
- usage_.used_in_words = marker.marked_words();
-
- int64_t mid1 = OS::GetCurrentMonotonicMicros();
-
- // Abandon the remainder of the bump allocation block.
- AbandonBumpAllocation();
- // Reset the freelists and setup sweeping.
- freelist_[HeapPage::kData].Reset();
- freelist_[HeapPage::kExecutable].Reset();
-
- int64_t mid2 = OS::GetCurrentMonotonicMicros();
- int64_t mid3 = 0;
-
- {
- if (FLAG_verify_before_gc) {
- OS::PrintErr("Verifying before sweeping...");
- heap_->VerifyGC(kAllowMarked);
- OS::PrintErr(" done.\n");
- }
-
- TIMELINE_FUNCTION_GC_DURATION(thread, "SweepLargeAndExecutablePages");
- GCSweeper sweeper;
-
- // During stop-the-world phases we should use bulk lock when adding
- // elements to the free list.
- MutexLocker mld(freelist_[HeapPage::kData].mutex());
- MutexLocker mle(freelist_[HeapPage::kExecutable].mutex());
-
- // Large and executable pages are always swept immediately.
- HeapPage* prev_page = NULL;
- HeapPage* page = large_pages_;
- while (page != NULL) {
- HeapPage* next_page = page->next();
- const intptr_t words_to_end = sweeper.SweepLargePage(page);
- if (words_to_end == 0) {
- FreeLargePage(page, prev_page);
- } else {
- TruncateLargePage(page, words_to_end << kWordSizeLog2);
- prev_page = page;
- }
- // Advance to the next page.
- page = next_page;
- }
-
- prev_page = NULL;
- page = exec_pages_;
- FreeList* freelist = &freelist_[HeapPage::kExecutable];
- while (page != NULL) {
- HeapPage* next_page = page->next();
- bool page_in_use = sweeper.SweepPage(page, freelist, true);
- if (page_in_use) {
- prev_page = page;
- } else {
- FreePage(page, prev_page);
- }
- // Advance to the next page.
- page = next_page;
- }
-
- mid3 = OS::GetCurrentMonotonicMicros();
- }
-
- if (compact) {
- Compact(thread);
- } else if (FLAG_concurrent_sweep) {
- ConcurrentSweep(isolate);
- } else {
- BlockingSweep();
- }
-
- // Make code pages read-only.
- WriteProtectCode(true);
-
- int64_t end = OS::GetCurrentMonotonicMicros();
-
- // Record signals for growth control. Include size of external allocations.
- page_space_controller_.EvaluateGarbageCollection(
- usage_before, GetCurrentUsage(), start, end);
-
- int64_t mark_micros = mid3 - start;
- if (mark_micros == 0) {
- mark_micros = 1; // Prevent division by zero.
- }
- mark_words_per_micro_ = usage_before.used_in_words / mark_micros;
- if (mark_words_per_micro_ == 0) {
- mark_words_per_micro_ = 1; // Prevent division by zero.
- }
-
- heap_->RecordTime(kConcurrentSweep, pre_safe_point - pre_wait_for_sweepers);
- heap_->RecordTime(kSafePoint, start - pre_safe_point);
- heap_->RecordTime(kMarkObjects, mid1 - start);
- heap_->RecordTime(kResetFreeLists, mid2 - mid1);
- heap_->RecordTime(kSweepPages, mid3 - mid2);
- heap_->RecordTime(kSweepLargePages, end - mid3);
-
- if (FLAG_print_free_list_after_gc) {
- OS::PrintErr("Data Freelist (after GC):\n");
- freelist_[HeapPage::kData].Print();
- OS::PrintErr("Executable Freelist (after GC):\n");
- freelist_[HeapPage::kExecutable].Print();
- }
-
- UpdateMaxUsed();
- if (heap_ != NULL) {
- heap_->UpdateGlobalMaxUsed();
- }
+ CollectGarbageAtSafepoint(compact, pre_wait_for_sweepers, pre_safe_point);
}
// Done, reset the task count.
@@ -1076,6 +932,158 @@
}
}
+void PageSpace::CollectGarbageAtSafepoint(bool compact,
+ int64_t pre_wait_for_sweepers,
+ int64_t pre_safe_point) {
+ Thread* thread = Thread::Current();
+ ASSERT(thread->IsAtSafepoint());
+ Isolate* isolate = heap_->isolate();
+ ASSERT(isolate == Isolate::Current());
+
+ const int64_t start = OS::GetCurrentMonotonicMicros();
+
+ NOT_IN_PRODUCT(isolate->class_table()->ResetCountersOld());
+ // Perform various cleanup that relies on no tasks interfering.
+ isolate->class_table()->FreeOldTables();
+
+ NoSafepointScope no_safepoints;
+
+ if (FLAG_print_free_list_before_gc) {
+ OS::PrintErr("Data Freelist (before GC):\n");
+ freelist_[HeapPage::kData].Print();
+ OS::PrintErr("Executable Freelist (before GC):\n");
+ freelist_[HeapPage::kExecutable].Print();
+ }
+
+ if (FLAG_verify_before_gc) {
+ OS::PrintErr("Verifying before marking...");
+ heap_->VerifyGC();
+ OS::PrintErr(" done.\n");
+ }
+
+ // Make code pages writable.
+ WriteProtectCode(false);
+
+ // Save old value before GCMarker visits the weak persistent handles.
+ SpaceUsage usage_before = GetCurrentUsage();
+
+ // Mark all reachable old-gen objects.
+#if defined(PRODUCT)
+ bool collect_code = FLAG_collect_code && ShouldCollectCode();
+#else
+ bool collect_code = FLAG_collect_code && ShouldCollectCode() &&
+ !isolate->HasAttemptedReload();
+#endif // !defined(PRODUCT)
+ GCMarker marker(heap_);
+ marker.MarkObjects(isolate, this, collect_code);
+ usage_.used_in_words = marker.marked_words();
+
+ int64_t mid1 = OS::GetCurrentMonotonicMicros();
+
+ // Abandon the remainder of the bump allocation block.
+ AbandonBumpAllocation();
+ // Reset the freelists and setup sweeping.
+ freelist_[HeapPage::kData].Reset();
+ freelist_[HeapPage::kExecutable].Reset();
+
+ int64_t mid2 = OS::GetCurrentMonotonicMicros();
+ int64_t mid3 = 0;
+
+ {
+ if (FLAG_verify_before_gc) {
+ OS::PrintErr("Verifying before sweeping...");
+ heap_->VerifyGC(kAllowMarked);
+ OS::PrintErr(" done.\n");
+ }
+
+ TIMELINE_FUNCTION_GC_DURATION(thread, "SweepLargeAndExecutablePages");
+ GCSweeper sweeper;
+
+ // During stop-the-world phases we should use bulk lock when adding
+ // elements to the free list.
+ MutexLocker mld(freelist_[HeapPage::kData].mutex());
+ MutexLocker mle(freelist_[HeapPage::kExecutable].mutex());
+
+ // Large and executable pages are always swept immediately.
+ HeapPage* prev_page = NULL;
+ HeapPage* page = large_pages_;
+ while (page != NULL) {
+ HeapPage* next_page = page->next();
+ const intptr_t words_to_end = sweeper.SweepLargePage(page);
+ if (words_to_end == 0) {
+ FreeLargePage(page, prev_page);
+ } else {
+ TruncateLargePage(page, words_to_end << kWordSizeLog2);
+ prev_page = page;
+ }
+ // Advance to the next page.
+ page = next_page;
+ }
+
+ prev_page = NULL;
+ page = exec_pages_;
+ FreeList* freelist = &freelist_[HeapPage::kExecutable];
+ while (page != NULL) {
+ HeapPage* next_page = page->next();
+ bool page_in_use = sweeper.SweepPage(page, freelist, true);
+ if (page_in_use) {
+ prev_page = page;
+ } else {
+ FreePage(page, prev_page);
+ }
+ // Advance to the next page.
+ page = next_page;
+ }
+
+ mid3 = OS::GetCurrentMonotonicMicros();
+ }
+
+ if (compact) {
+ Compact(thread);
+ } else if (FLAG_concurrent_sweep) {
+ ConcurrentSweep(isolate);
+ } else {
+ BlockingSweep();
+ }
+
+ // Make code pages read-only.
+ WriteProtectCode(true);
+
+ int64_t end = OS::GetCurrentMonotonicMicros();
+
+ // Record signals for growth control. Include size of external allocations.
+ page_space_controller_.EvaluateGarbageCollection(
+ usage_before, GetCurrentUsage(), start, end);
+
+ int64_t mark_micros = mid3 - start;
+ if (mark_micros == 0) {
+ mark_micros = 1; // Prevent division by zero.
+ }
+ mark_words_per_micro_ = usage_before.used_in_words / mark_micros;
+ if (mark_words_per_micro_ == 0) {
+ mark_words_per_micro_ = 1; // Prevent division by zero.
+ }
+
+ heap_->RecordTime(kConcurrentSweep, pre_safe_point - pre_wait_for_sweepers);
+ heap_->RecordTime(kSafePoint, start - pre_safe_point);
+ heap_->RecordTime(kMarkObjects, mid1 - start);
+ heap_->RecordTime(kResetFreeLists, mid2 - mid1);
+ heap_->RecordTime(kSweepPages, mid3 - mid2);
+ heap_->RecordTime(kSweepLargePages, end - mid3);
+
+ if (FLAG_print_free_list_after_gc) {
+ OS::PrintErr("Data Freelist (after GC):\n");
+ freelist_[HeapPage::kData].Print();
+ OS::PrintErr("Executable Freelist (after GC):\n");
+ freelist_[HeapPage::kExecutable].Print();
+ }
+
+ UpdateMaxUsed();
+ if (heap_ != NULL) {
+ heap_->UpdateGlobalMaxUsed();
+ }
+}
+
void PageSpace::BlockingSweep() {
TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "Sweep");
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 6a635d7..5db4eb9 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -387,6 +387,9 @@
void FreeLargePage(HeapPage* page, HeapPage* previous_page);
void FreePages(HeapPage* pages);
+ void CollectGarbageAtSafepoint(bool compact,
+ int64_t pre_wait_for_sweepers,
+ int64_t pre_safe_point);
void BlockingSweep();
void ConcurrentSweep(Isolate* isolate);
void Compact(Thread* thread);