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 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
198 if (idle_times_which_made_no_progress_per_mode_ >= | 197 if (idle_times_which_made_no_progress_per_mode_ >= |
199 kMaxNoProgressIdleTimesPerMode) { | 198 kMaxNoProgressIdleTimesPerMode) { |
200 return GCIdleTimeAction::Done(); | 199 return GCIdleTimeAction::Done(); |
201 } else { | 200 } else { |
202 idle_times_which_made_no_progress_per_mode_++; | 201 idle_times_which_made_no_progress_per_mode_++; |
203 return GCIdleTimeAction::Nothing(); | 202 return GCIdleTimeAction::Nothing(); |
204 } | 203 } |
205 } | 204 } |
206 | 205 |
207 | 206 |
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: | 207 // The following logic is implemented by the controller: |
263 // (1) If we don't have any idle time, do nothing, unless a context was | 208 // (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 | 209 // disposed, incremental marking is stopped, and the heap is small. Then do |
265 // a full GC. | 210 // a full GC. |
266 // (2) If the context disposal rate is high and we cannot perform a full GC, | 211 // (2) If the context disposal rate is high and we cannot perform a full GC, |
Hannes Payer (out of office)
2015/07/01 12:03:17
please update this comment
ulan
2015/07/01 12:51:48
Done.
| |
267 // we do nothing until the context disposal rate becomes lower. | 212 // 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 | 213 // (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. | 214 // 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 | 215 // (4) If there is currently no MarkCompact idle round going on, we start a |
271 // new idle round if enough garbage was created. Otherwise we do not perform | 216 // new idle round if enough garbage was created. Otherwise we do not perform |
272 // garbage collection to keep system utilization low. | 217 // garbage collection to keep system utilization low. |
273 // (5) If incremental marking is done, we perform a full garbage collection | 218 // (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 | 219 // 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 | 220 // round or if we are not allowed to start incremental marking. Otherwise we |
276 // do not perform garbage collection to keep system utilization low. | 221 // 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 | 222 // (6) If sweeping is in progress and we received a large enough idle time |
278 // request, we finalize sweeping here. | 223 // request, we finalize sweeping here. |
279 // (7) If incremental marking is in progress, we perform a marking step. Note, | 224 // (7) If incremental marking is in progress, we perform a marking step. Note, |
280 // that this currently may trigger a full garbage collection. | 225 // that this currently may trigger a full garbage collection. |
281 GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, | 226 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, |
282 const HeapState& heap_state, | 227 HeapState heap_state) { |
283 bool reduce_memory) { | |
284 if (static_cast<int>(idle_time_in_ms) <= 0) { | 228 if (static_cast<int>(idle_time_in_ms) <= 0) { |
285 if (heap_state.incremental_marking_stopped) { | 229 if (heap_state.incremental_marking_stopped) { |
286 if (ShouldDoContextDisposalMarkCompact( | 230 if (ShouldDoContextDisposalMarkCompact( |
287 heap_state.contexts_disposed, | 231 heap_state.contexts_disposed, |
288 heap_state.contexts_disposal_rate)) { | 232 heap_state.contexts_disposal_rate)) { |
289 return GCIdleTimeAction::FullGC(false); | 233 return GCIdleTimeAction::FullGC(); |
290 } | 234 } |
291 } | 235 } |
292 return GCIdleTimeAction::Nothing(); | 236 return GCIdleTimeAction::Nothing(); |
293 } | 237 } |
294 | 238 |
295 // We are in a context disposal GC scenario. Don't do anything if we do not | 239 // We are in a context disposal GC scenario. Don't do anything if we do not |
296 // get the right idle signal. | 240 // get the right idle signal. |
297 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, | 241 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, |
298 heap_state.contexts_disposal_rate)) { | 242 heap_state.contexts_disposal_rate)) { |
299 return NothingOrDone(); | 243 return NothingOrDone(); |
300 } | 244 } |
301 | 245 |
302 if (ShouldDoScavenge( | 246 if (ShouldDoScavenge( |
303 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, | 247 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, |
304 heap_state.used_new_space_size, | 248 heap_state.used_new_space_size, |
305 heap_state.scavenge_speed_in_bytes_per_ms, | 249 heap_state.scavenge_speed_in_bytes_per_ms, |
306 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { | 250 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { |
307 return GCIdleTimeAction::Scavenge(); | 251 return GCIdleTimeAction::Scavenge(); |
308 } | 252 } |
309 | 253 |
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) { | 254 if (heap_state.sweeping_in_progress) { |
319 if (heap_state.sweeping_completed) { | 255 if (heap_state.sweeping_completed) { |
320 return GCIdleTimeAction::FinalizeSweeping(); | 256 return GCIdleTimeAction::FinalizeSweeping(); |
321 } else { | 257 } else { |
322 return NothingOrDone(); | 258 return NothingOrDone(); |
323 } | 259 } |
324 } | 260 } |
325 | 261 |
326 if (!FLAG_incremental_marking || | 262 if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) { |
327 (heap_state.incremental_marking_stopped && | 263 return GCIdleTimeAction::Done(); |
328 !heap_state.can_start_incremental_marking && !reduce_memory)) { | |
329 return NothingOrDone(); | |
330 } | 264 } |
331 | 265 |
332 size_t step_size = EstimateMarkingStepSize( | 266 size_t step_size = EstimateMarkingStepSize( |
333 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), | 267 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), |
334 heap_state.incremental_marking_speed_in_bytes_per_ms); | 268 heap_state.incremental_marking_speed_in_bytes_per_ms); |
335 return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); | 269 return GCIdleTimeAction::IncrementalMarking(step_size); |
336 } | 270 } |
337 | 271 |
338 | 272 |
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 } | 273 } |
416 } | 274 } |
417 } | |
OLD | NEW |