// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/heap/sweeper.h" #include "vm/globals.h" #include "vm/heap/freelist.h" #include "vm/heap/heap.h" #include "vm/heap/pages.h" #include "vm/heap/safepoint.h" #include "vm/lockers.h" #include "vm/thread_pool.h" #include "vm/timeline.h" namespace dart { bool GCSweeper::SweepPage(OldPage* page, FreeList* freelist, bool locked) { ASSERT(!page->is_image_page()); // Keep track whether this page is still in use. intptr_t used_in_bytes = 0; bool is_executable = (page->type() == OldPage::kExecutable); uword start = page->object_start(); uword end = page->object_end(); uword current = start; while (current < end) { ObjectPtr raw_obj = UntaggedObject::FromAddr(current); ASSERT(OldPage::Of(raw_obj) == page); // These acquire operations balance release operations in array // truncaton, ensuring the writes creating the filler object are ordered // before the writes inserting the filler object into the freelist. uword tags = raw_obj->untag()->tags_.load(std::memory_order_acquire); intptr_t obj_size = raw_obj->untag()->HeapSize(tags); if (UntaggedObject::IsMarked(tags)) { // Found marked object. Clear the mark bit and update swept bytes. raw_obj->untag()->ClearMarkBit(); used_in_bytes += obj_size; } else { uword free_end = current + obj_size; while (free_end < end) { ObjectPtr next_obj = UntaggedObject::FromAddr(free_end); tags = next_obj->untag()->tags_.load(std::memory_order_acquire); if (UntaggedObject::IsMarked(tags)) { // Reached the end of the free block. break; } // Expand the free block by the size of this object. free_end += next_obj->untag()->HeapSize(tags); } obj_size = free_end - current; if (is_executable) { uword cursor = current; uword end = current + obj_size; while (cursor < end) { *reinterpret_cast(cursor) = kBreakInstructionFiller; cursor += kWordSize; } } else { #if defined(DEBUG) memset(reinterpret_cast(current), Heap::kZapByte, obj_size); #endif // DEBUG } if ((current != start) || (free_end != end)) { // Only add to the free list if not covering the whole page. if (locked) { freelist->FreeLocked(current, obj_size); } else { freelist->Free(current, obj_size); } } } current += obj_size; } ASSERT(current == end); page->set_used_in_bytes(used_in_bytes); return used_in_bytes != 0; // In use. } intptr_t GCSweeper::SweepLargePage(OldPage* page) { ASSERT(!page->is_image_page()); intptr_t words_to_end = 0; ObjectPtr raw_obj = UntaggedObject::FromAddr(page->object_start()); ASSERT(OldPage::Of(raw_obj) == page); if (raw_obj->untag()->IsMarked()) { raw_obj->untag()->ClearMarkBit(); words_to_end = (raw_obj->untag()->HeapSize() >> kWordSizeLog2); } #ifdef DEBUG // Array::MakeFixedLength creates trailing filler objects, // but they are always unreachable. Verify that they are not marked. uword current = UntaggedObject::ToAddr(raw_obj) + raw_obj->untag()->HeapSize(); uword end = page->object_end(); while (current < end) { ObjectPtr cur_obj = UntaggedObject::FromAddr(current); ASSERT(!cur_obj->untag()->IsMarked()); intptr_t obj_size = cur_obj->untag()->HeapSize(); memset(reinterpret_cast(current), Heap::kZapByte, obj_size); current += obj_size; } #endif // DEBUG return words_to_end; } class ConcurrentSweeperTask : public ThreadPool::Task { public: ConcurrentSweeperTask(IsolateGroup* isolate_group, PageSpace* old_space, OldPage* first, OldPage* last, OldPage* large_first, OldPage* large_last) : task_isolate_group_(isolate_group), old_space_(old_space), first_(first), last_(last), large_first_(large_first), large_last_(large_last) { ASSERT(task_isolate_group_ != NULL); ASSERT(first_ != NULL); ASSERT(old_space_ != NULL); ASSERT(last_ != NULL); MonitorLocker ml(old_space_->tasks_lock()); old_space_->set_tasks(old_space_->tasks() + 1); old_space_->set_phase(PageSpace::kSweepingLarge); } virtual void Run() { bool result = Thread::EnterIsolateGroupAsHelper( task_isolate_group_, Thread::kSweeperTask, /*bypass_safepoint=*/true); ASSERT(result); { Thread* thread = Thread::Current(); ASSERT(thread->BypassSafepoints()); // Or we should be checking in. TIMELINE_FUNCTION_GC_DURATION(thread, "ConcurrentSweep"); GCSweeper sweeper; OldPage* page = large_first_; OldPage* prev_page = NULL; while (page != NULL) { OldPage* next_page; if (page == large_last_) { // Don't access page->next(), which would be a race with mutator // allocating new pages. next_page = NULL; } else { next_page = page->next(); } ASSERT(page->type() == OldPage::kData); const intptr_t words_to_end = sweeper.SweepLargePage(page); if (words_to_end == 0) { old_space_->FreeLargePage(page, prev_page); } else { old_space_->TruncateLargePage(page, words_to_end << kWordSizeLog2); prev_page = page; } page = next_page; } { MonitorLocker ml(old_space_->tasks_lock()); ASSERT(old_space_->phase() == PageSpace::kSweepingLarge); old_space_->set_phase(PageSpace::kSweepingRegular); ml.NotifyAll(); } intptr_t shard = 0; const intptr_t num_shards = Utils::Maximum(FLAG_scavenger_tasks, 1); page = first_; prev_page = NULL; while (page != NULL) { OldPage* next_page; if (page == last_) { // Don't access page->next(), which would be a race with mutator // allocating new pages. next_page = NULL; } else { next_page = page->next(); } ASSERT(page->type() == OldPage::kData); shard = (shard + 1) % num_shards; bool page_in_use = sweeper.SweepPage(page, old_space_->DataFreeList(shard), false); if (page_in_use) { prev_page = page; } else { old_space_->FreePage(page, prev_page); } { // Notify the mutator thread that we have added elements to the free // list or that more capacity is available. MonitorLocker ml(old_space_->tasks_lock()); ml.Notify(); } page = next_page; } } // Exit isolate cleanly *before* notifying it, to avoid shutdown race. Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/true); // This sweeper task is done. Notify the original isolate. { MonitorLocker ml(old_space_->tasks_lock()); old_space_->set_tasks(old_space_->tasks() - 1); ASSERT(old_space_->phase() == PageSpace::kSweepingRegular); old_space_->set_phase(PageSpace::kDone); ml.NotifyAll(); } } private: IsolateGroup* task_isolate_group_; PageSpace* old_space_; OldPage* first_; OldPage* last_; OldPage* large_first_; OldPage* large_last_; }; void GCSweeper::SweepConcurrent(IsolateGroup* isolate_group, OldPage* first, OldPage* last, OldPage* large_first, OldPage* large_last, FreeList* freelist) { bool result = Dart::thread_pool()->Run( isolate_group, isolate_group->heap()->old_space(), first, last, large_first, large_last); ASSERT(result); } } // namespace dart