| 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); |
| 53 PrintF("sweeping_in_progress=%d ", sweeping_in_progress); | 54 PrintF("sweeping_in_progress=%d ", sweeping_in_progress); |
| 54 PrintF("has_low_allocation_rate=%d", has_low_allocation_rate); | 55 PrintF("has_low_allocation_rate=%d", has_low_allocation_rate); |
| 55 PrintF("mark_compact_speed=%" V8_PTR_PREFIX "d ", | 56 PrintF("mark_compact_speed=%" V8_PTR_PREFIX "d ", |
| 56 mark_compact_speed_in_bytes_per_ms); | 57 mark_compact_speed_in_bytes_per_ms); |
| 57 PrintF("incremental_marking_speed=%" V8_PTR_PREFIX "d ", | 58 PrintF("incremental_marking_speed=%" V8_PTR_PREFIX "d ", |
| 58 incremental_marking_speed_in_bytes_per_ms); | 59 incremental_marking_speed_in_bytes_per_ms); |
| 59 PrintF("scavenge_speed=%" V8_PTR_PREFIX "d ", scavenge_speed_in_bytes_per_ms); | 60 PrintF("scavenge_speed=%" V8_PTR_PREFIX "d ", scavenge_speed_in_bytes_per_ms); |
| 60 PrintF("new_space_size=%" V8_PTR_PREFIX "d ", used_new_space_size); | 61 PrintF("new_space_size=%" V8_PTR_PREFIX "d ", used_new_space_size); |
| 61 PrintF("new_space_capacity=%" V8_PTR_PREFIX "d ", new_space_capacity); | 62 PrintF("new_space_capacity=%" V8_PTR_PREFIX "d ", new_space_capacity); |
| 62 PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", | 63 PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 187 | 188 |
| 188 | 189 |
| 189 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( | 190 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( |
| 190 size_t idle_time_in_ms) { | 191 size_t idle_time_in_ms) { |
| 191 // TODO(jochen): Estimate the time it will take to build the object groups. | 192 // TODO(jochen): Estimate the time it will take to build the object groups. |
| 192 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; | 193 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; |
| 193 } | 194 } |
| 194 | 195 |
| 195 | 196 |
| 196 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { | 197 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { |
| 197 if (idle_times_which_made_no_progress_ >= kMaxNoProgressIdleTimes) { | 198 if (idle_times_which_made_no_progress_per_mode_ >= |
| 199 kMaxNoProgressIdleTimesPerMode) { |
| 198 return GCIdleTimeAction::Done(); | 200 return GCIdleTimeAction::Done(); |
| 199 } else { | 201 } else { |
| 200 idle_times_which_made_no_progress_++; | 202 idle_times_which_made_no_progress_per_mode_++; |
| 201 return GCIdleTimeAction::Nothing(); | 203 return GCIdleTimeAction::Nothing(); |
| 202 } | 204 } |
| 203 } | 205 } |
| 204 | 206 |
| 205 | 207 |
| 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 |
| 206 // The following logic is implemented by the controller: | 262 // The following logic is implemented by the controller: |
| 207 // (1) If we don't have any idle time, do nothing, unless a context was | 263 // (1) If we don't have any idle time, do nothing, unless a context was |
| 208 // disposed, incremental marking is stopped, and the heap is small. Then do | 264 // disposed, incremental marking is stopped, and the heap is small. Then do |
| 209 // a full GC. | 265 // a full GC. |
| 210 // (2) If the context disposal rate is high and we cannot perform a full GC, | 266 // (2) If the context disposal rate is high and we cannot perform a full GC, |
| 211 // we do nothing until the context disposal rate becomes lower. | 267 // we do nothing until the context disposal rate becomes lower. |
| 212 // (3) If the new space is almost full and we can affort a scavenge or if the | 268 // (3) If the new space is almost full and we can affort a scavenge or if the |
| 213 // next scavenge will very likely take long, then a scavenge is performed. | 269 // next scavenge will very likely take long, then a scavenge is performed. |
| 214 // (4) If sweeping is in progress and we received a large enough idle time | 270 // (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 |
| 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 |
| 215 // request, we finalize sweeping here. | 278 // request, we finalize sweeping here. |
| 216 // (5) If incremental marking is in progress, we perform a marking step. Note, | 279 // (7) If incremental marking is in progress, we perform a marking step. Note, |
| 217 // that this currently may trigger a full garbage collection. | 280 // that this currently may trigger a full garbage collection. |
| 218 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, | 281 GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, |
| 219 HeapState heap_state) { | 282 const HeapState& heap_state, |
| 283 bool reduce_memory) { |
| 220 if (static_cast<int>(idle_time_in_ms) <= 0) { | 284 if (static_cast<int>(idle_time_in_ms) <= 0) { |
| 221 if (heap_state.incremental_marking_stopped) { | 285 if (heap_state.incremental_marking_stopped) { |
| 222 if (ShouldDoContextDisposalMarkCompact( | 286 if (ShouldDoContextDisposalMarkCompact( |
| 223 heap_state.contexts_disposed, | 287 heap_state.contexts_disposed, |
| 224 heap_state.contexts_disposal_rate)) { | 288 heap_state.contexts_disposal_rate)) { |
| 225 return GCIdleTimeAction::FullGC(); | 289 return GCIdleTimeAction::FullGC(false); |
| 226 } | 290 } |
| 227 } | 291 } |
| 228 return GCIdleTimeAction::Nothing(); | 292 return GCIdleTimeAction::Nothing(); |
| 229 } | 293 } |
| 230 | 294 |
| 231 // We are in a context disposal GC scenario. Don't do anything if we do not | 295 // We are in a context disposal GC scenario. Don't do anything if we do not |
| 232 // get the right idle signal. | 296 // get the right idle signal. |
| 233 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, | 297 if (ShouldDoContextDisposalMarkCompact(heap_state.contexts_disposed, |
| 234 heap_state.contexts_disposal_rate)) { | 298 heap_state.contexts_disposal_rate)) { |
| 235 return NothingOrDone(); | 299 return NothingOrDone(); |
| 236 } | 300 } |
| 237 | 301 |
| 238 if (ShouldDoScavenge( | 302 if (ShouldDoScavenge( |
| 239 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, | 303 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, |
| 240 heap_state.used_new_space_size, | 304 heap_state.used_new_space_size, |
| 241 heap_state.scavenge_speed_in_bytes_per_ms, | 305 heap_state.scavenge_speed_in_bytes_per_ms, |
| 242 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { | 306 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { |
| 243 return GCIdleTimeAction::Scavenge(); | 307 return GCIdleTimeAction::Scavenge(); |
| 244 } | 308 } |
| 245 | 309 |
| 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 |
| 246 if (heap_state.sweeping_in_progress) { | 318 if (heap_state.sweeping_in_progress) { |
| 247 if (heap_state.sweeping_completed) { | 319 if (heap_state.sweeping_completed) { |
| 248 return GCIdleTimeAction::FinalizeSweeping(); | 320 return GCIdleTimeAction::FinalizeSweeping(); |
| 249 } else { | 321 } else { |
| 250 return NothingOrDone(); | 322 return NothingOrDone(); |
| 251 } | 323 } |
| 252 } | 324 } |
| 253 | 325 |
| 254 if (!FLAG_incremental_marking || heap_state.incremental_marking_stopped) { | 326 if (!FLAG_incremental_marking || |
| 255 return GCIdleTimeAction::Done(); | 327 (heap_state.incremental_marking_stopped && |
| 328 !heap_state.can_start_incremental_marking && !reduce_memory)) { |
| 329 return NothingOrDone(); |
| 256 } | 330 } |
| 257 | 331 |
| 258 size_t step_size = EstimateMarkingStepSize( | 332 size_t step_size = EstimateMarkingStepSize( |
| 259 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), | 333 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), |
| 260 heap_state.incremental_marking_speed_in_bytes_per_ms); | 334 heap_state.incremental_marking_speed_in_bytes_per_ms); |
| 261 return GCIdleTimeAction::IncrementalMarking(step_size); | 335 return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); |
| 262 } | 336 } |
| 263 | 337 |
| 264 | 338 |
| 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_; |
| 265 } | 415 } |
| 266 } | 416 } |
| 417 } |
| OLD | NEW |