1 | // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/heap/sweeper.h" |
6 | |
7 | #include "vm/globals.h" |
8 | #include "vm/heap/freelist.h" |
9 | #include "vm/heap/heap.h" |
10 | #include "vm/heap/pages.h" |
11 | #include "vm/heap/safepoint.h" |
12 | #include "vm/lockers.h" |
13 | #include "vm/thread_pool.h" |
14 | #include "vm/timeline.h" |
15 | |
16 | namespace dart { |
17 | |
18 | bool GCSweeper::SweepPage(OldPage* page, FreeList* freelist, bool locked) { |
19 | ASSERT(!page->is_image_page()); |
20 | |
21 | // Keep track whether this page is still in use. |
22 | intptr_t used_in_bytes = 0; |
23 | |
24 | bool is_executable = (page->type() == OldPage::kExecutable); |
25 | uword start = page->object_start(); |
26 | uword end = page->object_end(); |
27 | uword current = start; |
28 | |
29 | while (current < end) { |
30 | intptr_t obj_size; |
31 | ObjectPtr raw_obj = ObjectLayout::FromAddr(current); |
32 | ASSERT(OldPage::Of(raw_obj) == page); |
33 | if (raw_obj->ptr()->IsMarked()) { |
34 | // Found marked object. Clear the mark bit and update swept bytes. |
35 | raw_obj->ptr()->ClearMarkBit(); |
36 | obj_size = raw_obj->ptr()->HeapSize(); |
37 | used_in_bytes += obj_size; |
38 | } else { |
39 | uword free_end = current + raw_obj->ptr()->HeapSize(); |
40 | while (free_end < end) { |
41 | ObjectPtr next_obj = ObjectLayout::FromAddr(free_end); |
42 | if (next_obj->ptr()->IsMarked()) { |
43 | // Reached the end of the free block. |
44 | break; |
45 | } |
46 | // Expand the free block by the size of this object. |
47 | free_end += next_obj->ptr()->HeapSize(); |
48 | } |
49 | obj_size = free_end - current; |
50 | if (is_executable) { |
51 | uword cursor = current; |
52 | uword end = current + obj_size; |
53 | while (cursor < end) { |
54 | *reinterpret_cast<uword*>(cursor) = kBreakInstructionFiller; |
55 | cursor += kWordSize; |
56 | } |
57 | } else { |
58 | #if defined(DEBUG) |
59 | memset(reinterpret_cast<void*>(current), Heap::kZapByte, obj_size); |
60 | #endif // DEBUG |
61 | } |
62 | if ((current != start) || (free_end != end)) { |
63 | // Only add to the free list if not covering the whole page. |
64 | if (locked) { |
65 | freelist->FreeLocked(current, obj_size); |
66 | } else { |
67 | freelist->Free(current, obj_size); |
68 | } |
69 | } |
70 | } |
71 | current += obj_size; |
72 | } |
73 | ASSERT(current == end); |
74 | |
75 | page->set_used_in_bytes(used_in_bytes); |
76 | return used_in_bytes != 0; // In use. |
77 | } |
78 | |
79 | intptr_t GCSweeper::SweepLargePage(OldPage* page) { |
80 | ASSERT(!page->is_image_page()); |
81 | |
82 | intptr_t words_to_end = 0; |
83 | ObjectPtr raw_obj = ObjectLayout::FromAddr(page->object_start()); |
84 | ASSERT(OldPage::Of(raw_obj) == page); |
85 | if (raw_obj->ptr()->IsMarked()) { |
86 | raw_obj->ptr()->ClearMarkBit(); |
87 | words_to_end = (raw_obj->ptr()->HeapSize() >> kWordSizeLog2); |
88 | } |
89 | #ifdef DEBUG |
90 | // Array::MakeFixedLength creates trailing filler objects, |
91 | // but they are always unreachable. Verify that they are not marked. |
92 | uword current = ObjectLayout::ToAddr(raw_obj) + raw_obj->ptr()->HeapSize(); |
93 | uword end = page->object_end(); |
94 | while (current < end) { |
95 | ObjectPtr cur_obj = ObjectLayout::FromAddr(current); |
96 | ASSERT(!cur_obj->ptr()->IsMarked()); |
97 | intptr_t obj_size = cur_obj->ptr()->HeapSize(); |
98 | memset(reinterpret_cast<void*>(current), Heap::kZapByte, obj_size); |
99 | current += obj_size; |
100 | } |
101 | #endif // DEBUG |
102 | return words_to_end; |
103 | } |
104 | |
105 | class ConcurrentSweeperTask : public ThreadPool::Task { |
106 | public: |
107 | ConcurrentSweeperTask(IsolateGroup* isolate_group, |
108 | PageSpace* old_space, |
109 | OldPage* first, |
110 | OldPage* last, |
111 | OldPage* large_first, |
112 | OldPage* large_last) |
113 | : task_isolate_group_(isolate_group), |
114 | old_space_(old_space), |
115 | first_(first), |
116 | last_(last), |
117 | large_first_(large_first), |
118 | large_last_(large_last) { |
119 | ASSERT(task_isolate_group_ != NULL); |
120 | ASSERT(first_ != NULL); |
121 | ASSERT(old_space_ != NULL); |
122 | ASSERT(last_ != NULL); |
123 | MonitorLocker ml(old_space_->tasks_lock()); |
124 | old_space_->set_tasks(old_space_->tasks() + 1); |
125 | old_space_->set_phase(PageSpace::kSweepingLarge); |
126 | } |
127 | |
128 | virtual void Run() { |
129 | bool result = Thread::EnterIsolateGroupAsHelper( |
130 | task_isolate_group_, Thread::kSweeperTask, /*bypass_safepoint=*/true); |
131 | ASSERT(result); |
132 | { |
133 | Thread* thread = Thread::Current(); |
134 | ASSERT(thread->BypassSafepoints()); // Or we should be checking in. |
135 | TIMELINE_FUNCTION_GC_DURATION(thread, "ConcurrentSweep" ); |
136 | GCSweeper sweeper; |
137 | |
138 | OldPage* page = large_first_; |
139 | OldPage* prev_page = NULL; |
140 | while (page != NULL) { |
141 | OldPage* next_page; |
142 | if (page == large_last_) { |
143 | // Don't access page->next(), which would be a race with mutator |
144 | // allocating new pages. |
145 | next_page = NULL; |
146 | } else { |
147 | next_page = page->next(); |
148 | } |
149 | ASSERT(page->type() == OldPage::kData); |
150 | const intptr_t words_to_end = sweeper.SweepLargePage(page); |
151 | if (words_to_end == 0) { |
152 | old_space_->FreeLargePage(page, prev_page); |
153 | } else { |
154 | old_space_->TruncateLargePage(page, words_to_end << kWordSizeLog2); |
155 | prev_page = page; |
156 | } |
157 | page = next_page; |
158 | } |
159 | |
160 | { |
161 | MonitorLocker ml(old_space_->tasks_lock()); |
162 | ASSERT(old_space_->phase() == PageSpace::kSweepingLarge); |
163 | old_space_->set_phase(PageSpace::kSweepingRegular); |
164 | ml.NotifyAll(); |
165 | } |
166 | |
167 | intptr_t shard = 0; |
168 | const intptr_t num_shards = Utils::Maximum(FLAG_scavenger_tasks, 1); |
169 | page = first_; |
170 | prev_page = NULL; |
171 | while (page != NULL) { |
172 | OldPage* next_page; |
173 | if (page == last_) { |
174 | // Don't access page->next(), which would be a race with mutator |
175 | // allocating new pages. |
176 | next_page = NULL; |
177 | } else { |
178 | next_page = page->next(); |
179 | } |
180 | ASSERT(page->type() == OldPage::kData); |
181 | shard = (shard + 1) % num_shards; |
182 | bool page_in_use = |
183 | sweeper.SweepPage(page, old_space_->DataFreeList(shard), false); |
184 | if (page_in_use) { |
185 | prev_page = page; |
186 | } else { |
187 | old_space_->FreePage(page, prev_page); |
188 | } |
189 | { |
190 | // Notify the mutator thread that we have added elements to the free |
191 | // list or that more capacity is available. |
192 | MonitorLocker ml(old_space_->tasks_lock()); |
193 | ml.Notify(); |
194 | } |
195 | page = next_page; |
196 | } |
197 | } |
198 | // Exit isolate cleanly *before* notifying it, to avoid shutdown race. |
199 | Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/true); |
200 | // This sweeper task is done. Notify the original isolate. |
201 | { |
202 | MonitorLocker ml(old_space_->tasks_lock()); |
203 | old_space_->set_tasks(old_space_->tasks() - 1); |
204 | ASSERT(old_space_->phase() == PageSpace::kSweepingRegular); |
205 | old_space_->set_phase(PageSpace::kDone); |
206 | ml.NotifyAll(); |
207 | } |
208 | } |
209 | |
210 | private: |
211 | IsolateGroup* task_isolate_group_; |
212 | PageSpace* old_space_; |
213 | OldPage* first_; |
214 | OldPage* last_; |
215 | OldPage* large_first_; |
216 | OldPage* large_last_; |
217 | }; |
218 | |
219 | void GCSweeper::SweepConcurrent(IsolateGroup* isolate_group, |
220 | OldPage* first, |
221 | OldPage* last, |
222 | OldPage* large_first, |
223 | OldPage* large_last, |
224 | FreeList* freelist) { |
225 | bool result = Dart::thread_pool()->Run<ConcurrentSweeperTask>( |
226 | isolate_group, isolate_group->heap()->old_space(), first, last, |
227 | large_first, large_last); |
228 | ASSERT(result); |
229 | } |
230 | |
231 | } // namespace dart |
232 | |