OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/flags.h" | 5 #include "src/flags.h" |
6 #include "src/heap/gc-idle-time-handler.h" | 6 #include "src/heap/gc-idle-time-handler.h" |
7 #include "src/heap/gc-tracer.h" | 7 #include "src/heap/gc-tracer.h" |
8 #include "src/utils.h" | 8 #include "src/utils.h" |
9 | 9 |
10 namespace v8 { | 10 namespace v8 { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 break; | 43 break; |
44 } | 44 } |
45 } | 45 } |
46 | 46 |
47 | 47 |
48 void GCIdleTimeHandler::HeapState::Print() { | 48 void GCIdleTimeHandler::HeapState::Print() { |
49 PrintF("contexts_disposed=%d ", contexts_disposed); | 49 PrintF("contexts_disposed=%d ", contexts_disposed); |
50 PrintF("contexts_disposal_rate=%f ", contexts_disposal_rate); | 50 PrintF("contexts_disposal_rate=%f ", contexts_disposal_rate); |
51 PrintF("size_of_objects=%" V8_PTR_PREFIX "d ", size_of_objects); | 51 PrintF("size_of_objects=%" V8_PTR_PREFIX "d ", size_of_objects); |
52 PrintF("incremental_marking_stopped=%d ", incremental_marking_stopped); | 52 PrintF("incremental_marking_stopped=%d ", incremental_marking_stopped); |
53 PrintF("can_start_incremental_marking=%d ", can_start_incremental_marking); | |
54 PrintF("sweeping_in_progress=%d ", sweeping_in_progress); | 53 PrintF("sweeping_in_progress=%d ", sweeping_in_progress); |
55 PrintF("has_low_allocation_rate=%d", has_low_allocation_rate); | 54 PrintF("has_low_allocation_rate=%d", has_low_allocation_rate); |
56 PrintF("mark_compact_speed=%" V8_PTR_PREFIX "d ", | 55 PrintF("mark_compact_speed=%" V8_PTR_PREFIX "d ", |
57 mark_compact_speed_in_bytes_per_ms); | 56 mark_compact_speed_in_bytes_per_ms); |
58 PrintF("incremental_marking_speed=%" V8_PTR_PREFIX "d ", | 57 PrintF("incremental_marking_speed=%" V8_PTR_PREFIX "d ", |
59 incremental_marking_speed_in_bytes_per_ms); | 58 incremental_marking_speed_in_bytes_per_ms); |
60 PrintF("scavenge_speed=%" V8_PTR_PREFIX "d ", scavenge_speed_in_bytes_per_ms); | 59 PrintF("scavenge_speed=%" V8_PTR_PREFIX "d ", scavenge_speed_in_bytes_per_ms); |
61 PrintF("new_space_size=%" V8_PTR_PREFIX "d ", used_new_space_size); | 60 PrintF("new_space_size=%" V8_PTR_PREFIX "d ", used_new_space_size); |
62 PrintF("new_space_capacity=%" V8_PTR_PREFIX "d ", new_space_capacity); | 61 PrintF("new_space_capacity=%" V8_PTR_PREFIX "d ", new_space_capacity); |
63 PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", | 62 PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 | 187 |
189 | 188 |
190 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( | 189 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( |
191 size_t idle_time_in_ms) { | 190 size_t idle_time_in_ms) { |
192 // TODO(jochen): Estimate the time it will take to build the object groups. | 191 // TODO(jochen): Estimate the time it will take to build the object groups. |
193 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; | 192 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; |
194 } | 193 } |
195 | 194 |
196 | 195 |
197 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { | 196 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { |
198 if (idle_times_which_made_no_progress_per_mode_ >= | 197 if (idle_times_which_made_no_progress_ >= kMaxNoProgressIdleTimes) { |
199 kMaxNoProgressIdleTimesPerMode) { | |
200 return GCIdleTimeAction::Done(); | 198 return GCIdleTimeAction::Done(); |
201 } else { | 199 } else { |
202 idle_times_which_made_no_progress_per_mode_++; | 200 idle_times_which_made_no_progress_++; |
203 return GCIdleTimeAction::Nothing(); | 201 return GCIdleTimeAction::Nothing(); |
204 } | 202 } |
205 } | 203 } |
206 | 204 |
207 | 205 |
208 // The idle time handler has three modes and transitions between them | |
209 // as shown in the diagram: | |
210 // | |
211 // kReduceLatency -----> kReduceMemory -----> kDone | |
212 // ^ ^ | | | |
213 // | | | | | |
214 // | +------------------+ | | |
215 // | | | |
216 // +----------------------------------------+ | |
217 // | |
218 // In kReduceLatency mode the handler only starts incremental marking | |
219 // if can_start_incremental_marking is false. | |
220 // In kReduceMemory mode the handler can force a new GC cycle by starting | |
221 // incremental marking even if can_start_incremental_marking is false. It can | |
222 // cause at most X idle GCs. | |
223 // In kDone mode the idle time handler does nothing. | |
224 // | |
225 // The initial mode is kReduceLatency. | |
226 // | |
227 // kReduceLatency => kReduceMemory transition happens if there were Y | |
228 // consecutive long idle notifications without any mutator GC. This is our | |
229 // notion of "mutator is idle". | |
230 // | |
231 // kReduceMemory => kDone transition happens after X idle GCs. | |
232 // | |
233 // kReduceMemory => kReduceLatency transition happens if N mutator GCs | |
234 // were performed meaning that the mutator is active. | |
235 // | |
236 // kDone => kReduceLatency transition happens if there were M mutator GCs or | |
237 // context was disposed. | |
238 // | |
239 // X = kMaxIdleMarkCompacts | |
240 // Y = kLongIdleNotificationsBeforeMutatorIsIdle | |
241 // N = #(idle GCs) | |
242 // M = kGCsBeforeMutatorIsActive | |
243 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, | |
244 HeapState heap_state) { | |
245 Mode next_mode = NextMode(heap_state); | |
246 | |
247 if (next_mode != mode_) { | |
248 mode_ = next_mode; | |
249 ResetCounters(); | |
250 } | |
251 | |
252 UpdateCounters(idle_time_in_ms); | |
253 | |
254 if (mode_ == kDone) { | |
255 return GCIdleTimeAction::Done(); | |
256 } else { | |
257 return Action(idle_time_in_ms, heap_state, mode_ == kReduceMemory); | |
258 } | |
259 } | |
260 | |
261 | |
262 // The following logic is implemented by the controller: | 206 // The following logic is implemented by the controller: |
263 // (1) If we don't have any idle time, do nothing, unless a context was | 207 // (1) If we don't have any idle time, do nothing, unless a context was |
264 // disposed, incremental marking is stopped, and the heap is small. Then do | 208 // disposed, incremental marking is stopped, and the heap is small. Then do |
265 // a full GC. | 209 // a full GC. |
266 // (2) If the context disposal rate is high and we cannot perform a full GC, | 210 // (2) If the context disposal rate is high and we cannot perform a full GC, |
267 // we do nothing until the context disposal rate becomes lower. | 211 // we do nothing until the context disposal rate becomes lower. |
268 // (3) If the new space is almost full and we can affort a scavenge or if the | 212 // (3) If the new space is almost full and we can affort a scavenge or if the |
269 // next scavenge will very likely take long, then a scavenge is performed. | 213 // next scavenge will very likely take long, then a scavenge is performed. |
270 // (4) If there is currently no MarkCompact idle round going on, we start a | 214 // (4) If sweeping is in progress and we received a large enough idle time |
271 // new idle round if enough garbage was created. Otherwise we do not perform | |
272 // garbage collection to keep system utilization low. | |
273 // (5) If incremental marking is done, we perform a full garbage collection | |
274 // if we are allowed to still do full garbage collections during this idle | |
275 // round or if we are not allowed to start incremental marking. Otherwise we | |
276 // do not perform garbage collection to keep system utilization low. | |
277 // (6) If sweeping is in progress and we received a large enough idle time | |
278 // request, we finalize sweeping here. | 215 // request, we finalize sweeping here. |
279 // (7) If incremental marking is in progress, we perform a marking step. Note, | 216 // (5) If incremental marking is in progress, we perform a marking step. Note, |
280 // that this currently may trigger a full garbage collection. | 217 // that this currently may trigger a full garbage collection. |
281 GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, | 218 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, |
282 const HeapState& heap_state, | 219 HeapState heap_state) { |
283 bool reduce_memory) { | |
284 if (static_cast<int>(idle_time_in_ms) <= 0) { | 220 if (static_cast<int>(idle_time_in_ms) <= 0) { |
285 if (heap_state.incremental_marking_stopped) { | 221 if (heap_state.incremental_marking_stopped) { |
286 if (ShouldDoContextDisposalMarkCompact( | 222 if (ShouldDoContextDisposalMarkCompact( |
287 heap_state.contexts_disposed, | 223 heap_state.contexts_disposed, |
288 heap_state.contexts_disposal_rate)) { | 224 heap_state.contexts_disposal_rate)) { |
289 return GCIdleTimeAction::FullGC(false); | 225 return GCIdleTimeAction::FullGC(); |
290 } | 226 } |
291 } | 227 } |
292 return GCIdleTimeAction::Nothing(); | 228 return GCIdleTimeAction::Nothing(); |
293 } | 229 } |
294 | 230 |
295 // We are in a context disposal GC scenario. Don't do anything if we do not | 231 // We are in a context disposal GC scenario. Don't do anything if we do not |
296 // get the right idle signal. | 232 // get the right idle signal. |
297 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, | 233 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, |
298 heap_state.contexts_disposal_rate)) { | 234 heap_state.contexts_disposal_rate)) { |
299 return NothingOrDone(); | 235 return NothingOrDone(); |
300 } | 236 } |
301 | 237 |
302 if (ShouldDoScavenge( | 238 if (ShouldDoScavenge( |
303 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, | 239 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, |
304 heap_state.used_new_space_size, | 240 heap_state.used_new_space_size, |
305 heap_state.scavenge_speed_in_bytes_per_ms, | 241 heap_state.scavenge_speed_in_bytes_per_ms, |
306 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { | 242 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { |
307 return GCIdleTimeAction::Scavenge(); | 243 return GCIdleTimeAction::Scavenge(); |
308 } | 244 } |
309 | 245 |
310 if (heap_state.incremental_marking_stopped && reduce_memory) { | |
311 if (ShouldDoMarkCompact(static_cast<size_t>(idle_time_in_ms), | |
312 heap_state.size_of_objects, | |
313 heap_state.mark_compact_speed_in_bytes_per_ms)) { | |
314 return GCIdleTimeAction::FullGC(reduce_memory); | |
315 } | |
316 } | |
317 | |
318 if (heap_state.sweeping_in_progress) { | 246 if (heap_state.sweeping_in_progress) { |
319 if (heap_state.sweeping_completed) { | 247 if (heap_state.sweeping_completed) { |
320 return GCIdleTimeAction::FinalizeSweeping(); | 248 return GCIdleTimeAction::FinalizeSweeping(); |
321 } else { | 249 } else { |
322 return NothingOrDone(); | 250 return NothingOrDone(); |
323 } | 251 } |
324 } | 252 } |
325 | 253 |
326 if (!FLAG_incremental_marking || | 254 if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) { |
327 (heap_state.incremental_marking_stopped && | 255 return GCIdleTimeAction::Done(); |
328 !heap_state.can_start_incremental_marking && !reduce_memory)) { | |
329 return NothingOrDone(); | |
330 } | 256 } |
331 | 257 |
332 size_t step_size = EstimateMarkingStepSize( | 258 size_t step_size = EstimateMarkingStepSize( |
333 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), | 259 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), |
334 heap_state.incremental_marking_speed_in_bytes_per_ms); | 260 heap_state.incremental_marking_speed_in_bytes_per_ms); |
335 return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); | 261 return GCIdleTimeAction::IncrementalMarking(step_size); |
336 } | 262 } |
337 | 263 |
338 | 264 |
339 void GCIdleTimeHandler::UpdateCounters(double idle_time_in_ms) { | |
340 if (mode_ == kReduceLatency) { | |
341 int gcs = scavenges_ + mark_compacts_; | |
342 if (gcs > 0) { | |
343 // There was a GC since the last notification. | |
344 long_idle_notifications_ = 0; | |
345 background_idle_notifications_ = 0; | |
346 } | |
347 idle_mark_compacts_ = 0; | |
348 mark_compacts_ = 0; | |
349 scavenges_ = 0; | |
350 if (idle_time_in_ms >= kMinBackgroundIdleTime) { | |
351 background_idle_notifications_++; | |
352 } else if (idle_time_in_ms >= kMinLongIdleTime) { | |
353 long_idle_notifications_++; | |
354 } | |
355 } | |
356 } | |
357 | |
358 | |
359 void GCIdleTimeHandler::ResetCounters() { | |
360 long_idle_notifications_ = 0; | |
361 background_idle_notifications_ = 0; | |
362 idle_mark_compacts_ = 0; | |
363 mark_compacts_ = 0; | |
364 scavenges_ = 0; | |
365 idle_times_which_made_no_progress_per_mode_ = 0; | |
366 } | |
367 | |
368 | |
369 bool GCIdleTimeHandler::IsMutatorActive(int contexts_disposed, | |
370 int mark_compacts) { | |
371 return contexts_disposed > 0 || | |
372 mark_compacts >= kMarkCompactsBeforeMutatorIsActive; | |
373 } | |
374 | |
375 | |
376 bool GCIdleTimeHandler::IsMutatorIdle(int long_idle_notifications, | |
377 int background_idle_notifications, | |
378 int mutator_gcs) { | |
379 return mutator_gcs == 0 && | |
380 (long_idle_notifications >= | |
381 kLongIdleNotificationsBeforeMutatorIsIdle || | |
382 background_idle_notifications >= | |
383 kBackgroundIdleNotificationsBeforeMutatorIsIdle); | |
384 } | |
385 | |
386 | |
387 GCIdleTimeHandler::Mode GCIdleTimeHandler::NextMode( | |
388 const HeapState& heap_state) { | |
389 DCHECK(mark_compacts_ >= idle_mark_compacts_); | |
390 int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; | |
391 switch (mode_) { | |
392 case kDone: | |
393 DCHECK(idle_mark_compacts_ == 0); | |
394 if (IsMutatorActive(heap_state.contexts_disposed, mark_compacts_)) { | |
395 return kReduceLatency; | |
396 } | |
397 break; | |
398 case kReduceLatency: | |
399 if (IsMutatorIdle(long_idle_notifications_, | |
400 background_idle_notifications_, mutator_gcs)) { | |
401 return kReduceMemory; | |
402 } | |
403 break; | |
404 case kReduceMemory: | |
405 if (idle_mark_compacts_ >= kMaxIdleMarkCompacts || | |
406 (idle_mark_compacts_ > 0 && !next_gc_likely_to_collect_more_)) { | |
407 return kDone; | |
408 } | |
409 if (mutator_gcs > idle_mark_compacts_) { | |
410 return kReduceLatency; | |
411 } | |
412 break; | |
413 } | |
414 return mode_; | |
415 } | 265 } |
416 } | 266 } |
417 } | |
OLD | NEW |