Chromium Code Reviews| 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 |