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/heap/gc-idle-time-handler.h" | 5 #include "src/heap/gc-idle-time-handler.h" |
| 6 #include "src/heap/gc-tracer.h" | 6 #include "src/heap/gc-tracer.h" |
| 7 #include "src/utils.h" | 7 #include "src/utils.h" |
| 8 | 8 |
| 9 namespace v8 { | 9 namespace v8 { |
| 10 namespace internal { | 10 namespace internal { |
| 11 | 11 |
| 12 const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9; | 12 const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9; |
| 13 const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000; | 13 const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000; |
| 14 const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000; | 14 const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000; |
| 15 const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 2; | |
| 16 const int GCIdleTimeHandler::kIdleScavengeThreshold = 5; | |
| 17 const double GCIdleTimeHandler::kHighContextDisposalRate = 100; | 15 const double GCIdleTimeHandler::kHighContextDisposalRate = 100; |
| 18 const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1; | 16 const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1; |
| 19 | 17 |
| 20 | 18 |
| 21 void GCIdleTimeAction::Print() { | 19 void GCIdleTimeAction::Print() { |
| 22 switch (type) { | 20 switch (type) { |
| 23 case DONE: | 21 case DONE: |
| 24 PrintF("done"); | 22 PrintF("done"); |
| 25 break; | 23 break; |
| 26 case DO_NOTHING: | 24 case DO_NOTHING: |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 180 } | 178 } |
| 181 | 179 |
| 182 | 180 |
| 183 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( | 181 bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( |
| 184 size_t idle_time_in_ms) { | 182 size_t idle_time_in_ms) { |
| 185 // TODO(jochen): Estimate the time it will take to build the object groups. | 183 // TODO(jochen): Estimate the time it will take to build the object groups. |
| 186 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; | 184 return idle_time_in_ms >= kMinTimeForOverApproximatingWeakClosureInMs; |
| 187 } | 185 } |
| 188 | 186 |
| 189 | 187 |
| 190 GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { | 188 // The idle time handler has three modes and transitions between them |
| 191 if (idle_times_which_made_no_progress_since_last_idle_round_ >= | 189 // as shown in the diagram: |
| 192 kMaxNoProgressIdleTimesPerIdleRound) { | 190 // |
| 191 // kReduceLatency -----> kReduceMemory -----> kDone | |
| 192 // ^ ^ | | | |
| 193 // | | | | | |
| 194 // | +------------------+ | | |
| 195 // | | | |
| 196 // +----------------------------------------+ | |
| 197 // | |
| 198 // In kReduceLatency mode the handler does only start incremental marking | |
|
Erik Corry
2015/05/05 21:38:52
does only start -> only starts
ulan
2015/05/06 09:47:19
Done.
| |
| 199 // if can_start_incremental_marking is false. | |
|
Erik Corry
2015/05/05 21:38:52
This contradiction indicates to me that can_start_
ulan
2015/05/06 09:47:19
Is should_start_incremental_marking better? I can
Erik Corry
2015/05/06 09:57:29
Yes, we can do it in a different CL. I was thinki
ulan
2015/05/07 10:33:30
Acknowledged.
| |
| 200 // In kReduceMemory mode the handler can force a new GC cycle by starting | |
| 201 // incremental marking even if can_start_incremental_marking is false. It can | |
| 202 // cause at most X idle GCs. | |
| 203 // In kDone mode the idle time handler does nothing. | |
| 204 // | |
| 205 // The initial mode is kReduceLatency. | |
| 206 // | |
| 207 // kReduceLatency => kReduceMemory transition happens if there were Y | |
| 208 // consecutive long idle notifications without any mutator GC. This is our | |
| 209 // notion of "mutator is idle". | |
| 210 // | |
| 211 // kReduceMemory => kDone transition happens after X idle GCs. | |
|
Erik Corry
2015/05/05 21:38:52
Probably not for this CL, but in the longer run I
ulan
2015/05/06 09:47:19
I will follow up with CL that uses the 'reduce_mem
| |
| 212 // | |
| 213 // kReduceMemory => kReduceLatency transition happens if N mutator GCs | |
|
Erik Corry
2015/05/05 21:38:52
Should N be 1? If any allocation-provoked GCs are
ulan
2015/05/06 09:47:19
This is to prevent back-and-forth transitions from
Erik Corry
2015/05/06 09:57:29
I can see that makes sense.
| |
| 214 // were performed meaning that the mutator is active. | |
| 215 // | |
| 216 // kDone => kReduceLatency transition happens if there were M mutator GCs or | |
| 217 // context was disposed. | |
| 218 // | |
| 219 // X = kMaxIdleMarkCompacts | |
| 220 // Y = kLongIdleNotificationsBeforeMutatorIsIdle | |
| 221 // N = #(idle GCs) | |
| 222 // M = kGCsBeforeMutatorIsActive | |
| 223 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, | |
| 224 HeapState heap_state) { | |
| 225 Mode next_mode = NextMode(heap_state); | |
| 226 | |
| 227 if (next_mode != mode_) { | |
| 228 mode_ = next_mode; | |
| 229 ResetCounters(); | |
| 230 } | |
| 231 | |
| 232 UpdateCounters(idle_time_in_ms); | |
| 233 | |
| 234 if (mode_ == kDone) { | |
| 193 return GCIdleTimeAction::Done(); | 235 return GCIdleTimeAction::Done(); |
| 194 } else { | 236 } else { |
| 195 idle_times_which_made_no_progress_since_last_idle_round_++; | 237 return Action(idle_time_in_ms, heap_state, mode_ == kReduceMemory); |
| 196 return GCIdleTimeAction::Nothing(); | |
| 197 } | 238 } |
| 198 } | 239 } |
| 199 | 240 |
| 200 | 241 |
| 201 // The following logic is implemented by the controller: | 242 // The following logic is implemented by the controller: |
| 202 // (1) If we don't have any idle time, do nothing, unless a context was | 243 // (1) If we don't have any idle time, do nothing, unless a context was |
| 203 // disposed, incremental marking is stopped, and the heap is small. Then do | 244 // disposed, incremental marking is stopped, and the heap is small. Then do |
| 204 // a full GC. | 245 // a full GC. |
| 205 // (2) If the new space is almost full and we can afford a Scavenge or if the | 246 // (2) If the new space is almost full and we can afford a Scavenge or if the |
| 206 // next Scavenge will very likely take long, then a Scavenge is performed. | 247 // next Scavenge will very likely take long, then a Scavenge is performed. |
| 207 // (3) If there is currently no MarkCompact idle round going on, we start a | 248 // (3) If incremental marking is done, we perform a full garbage collection |
| 208 // new idle round if enough garbage was created. Otherwise we do not perform | |
| 209 // garbage collection to keep system utilization low. | |
| 210 // (4) If incremental marking is done, we perform a full garbage collection | |
| 211 // if we are allowed to still do full garbage collections during this idle | 249 // if we are allowed to still do full garbage collections during this idle |
| 212 // round or if we are not allowed to start incremental marking. Otherwise we | 250 // round or if we are not allowed to start incremental marking. Otherwise we |
| 213 // do not perform garbage collection to keep system utilization low. | 251 // do not perform garbage collection to keep system utilization low. |
| 214 // (5) If sweeping is in progress and we received a large enough idle time | 252 // (4) If sweeping is in progress and we received a large enough idle time |
| 215 // request, we finalize sweeping here. | 253 // request, we finalize sweeping here. |
| 216 // (6) If incremental marking is in progress, we perform a marking step. Note, | 254 // (5) If incremental marking is in progress, we perform a marking step. Note, |
| 217 // that this currently may trigger a full garbage collection. | 255 // that this currently may trigger a full garbage collection. |
| 218 GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, | 256 GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, |
| 219 HeapState heap_state) { | 257 const HeapState& heap_state, |
| 258 bool reduce_memory) { | |
| 220 if (static_cast<int>(idle_time_in_ms) <= 0) { | 259 if (static_cast<int>(idle_time_in_ms) <= 0) { |
| 221 if (heap_state.contexts_disposed > 0) { | |
| 222 StartIdleRound(); | |
| 223 } | |
| 224 if (heap_state.incremental_marking_stopped) { | 260 if (heap_state.incremental_marking_stopped) { |
| 225 if (ShouldDoContextDisposalMarkCompact( | 261 if (ShouldDoContextDisposalMarkCompact( |
| 226 heap_state.contexts_disposed, | 262 heap_state.contexts_disposed, |
| 227 heap_state.contexts_disposal_rate)) { | 263 heap_state.contexts_disposal_rate)) { |
| 228 return GCIdleTimeAction::FullGC(); | 264 return GCIdleTimeAction::FullGC(false); |
| 229 } | 265 } |
| 230 } | 266 } |
| 231 return GCIdleTimeAction::Nothing(); | 267 return GCIdleTimeAction::Nothing(); |
| 232 } | 268 } |
| 233 | 269 |
| 234 if (ShouldDoScavenge( | 270 if (ShouldDoScavenge( |
| 235 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, | 271 static_cast<size_t>(idle_time_in_ms), heap_state.new_space_capacity, |
| 236 heap_state.used_new_space_size, | 272 heap_state.used_new_space_size, |
| 237 heap_state.scavenge_speed_in_bytes_per_ms, | 273 heap_state.scavenge_speed_in_bytes_per_ms, |
| 238 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { | 274 heap_state.new_space_allocation_throughput_in_bytes_per_ms)) { |
| 239 return GCIdleTimeAction::Scavenge(); | 275 return GCIdleTimeAction::Scavenge(); |
| 240 } | 276 } |
| 241 | 277 |
| 242 if (IsMarkCompactIdleRoundFinished()) { | 278 if (heap_state.incremental_marking_stopped && reduce_memory) { |
| 243 if (EnoughGarbageSinceLastIdleRound()) { | 279 if (ShouldDoMarkCompact(static_cast<size_t>(idle_time_in_ms), |
| 244 StartIdleRound(); | 280 heap_state.size_of_objects, |
| 245 } else { | 281 heap_state.mark_compact_speed_in_bytes_per_ms)) { |
| 246 return GCIdleTimeAction::Done(); | 282 return GCIdleTimeAction::FullGC(reduce_memory); |
| 247 } | 283 } |
| 248 } | 284 } |
| 249 | 285 |
| 250 if (heap_state.incremental_marking_stopped) { | |
| 251 if (ShouldDoMarkCompact(static_cast<size_t>(idle_time_in_ms), | |
| 252 heap_state.size_of_objects, | |
| 253 heap_state.mark_compact_speed_in_bytes_per_ms)) { | |
| 254 return GCIdleTimeAction::FullGC(); | |
| 255 } | |
| 256 } | |
| 257 if (heap_state.sweeping_in_progress) { | 286 if (heap_state.sweeping_in_progress) { |
| 258 if (heap_state.sweeping_completed) { | 287 if (heap_state.sweeping_completed) { |
| 259 return GCIdleTimeAction::FinalizeSweeping(); | 288 return GCIdleTimeAction::FinalizeSweeping(); |
| 260 } else { | 289 } else { |
| 261 return NothingOrDone(); | 290 return GCIdleTimeAction::Nothing(); |
| 262 } | 291 } |
| 263 } | 292 } |
| 293 | |
| 264 if (heap_state.incremental_marking_stopped && | 294 if (heap_state.incremental_marking_stopped && |
| 265 !heap_state.can_start_incremental_marking) { | 295 !heap_state.can_start_incremental_marking && !reduce_memory) { |
| 266 return NothingOrDone(); | 296 return GCIdleTimeAction::Nothing(); |
| 267 } | 297 } |
| 268 | 298 |
| 269 size_t step_size = EstimateMarkingStepSize( | 299 size_t step_size = EstimateMarkingStepSize( |
| 270 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), | 300 static_cast<size_t>(kIncrementalMarkingStepTimeInMs), |
| 271 heap_state.incremental_marking_speed_in_bytes_per_ms); | 301 heap_state.incremental_marking_speed_in_bytes_per_ms); |
| 272 return GCIdleTimeAction::IncrementalMarking(step_size); | 302 return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); |
| 303 } | |
| 304 | |
| 305 | |
| 306 void GCIdleTimeHandler::UpdateCounters(double idle_time_in_ms) { | |
| 307 if (mode_ == kReduceLatency) { | |
| 308 int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; | |
| 309 if (mutator_gcs > 0) { | |
| 310 ResetCounters(); | |
|
Erik Corry
2015/05/05 21:38:52
A few words on the intention here? What I can see
ulan
2015/05/06 09:47:19
I refactored this to be more explicit and added de
| |
| 311 } | |
| 312 long_idle_notifications_ += (idle_time_in_ms >= kMaxLongIdleTime) | |
|
Hannes Payer (out of office)
2015/05/05 20:51:16
This will never trigger, since idle_time_in_ms wil
ulan
2015/05/06 09:47:19
Changed this to kLargeLongIdleTime.
| |
| 313 ? kLongIdleNotificationsBeforeMutatorIsIdle | |
| 314 : (idle_time_in_ms >= kMinLongIdleTime); | |
|
Erik Corry
2015/05/05 21:38:52
Implicit cast of a bool to 0 or 1 is not good styl
ulan
2015/05/06 09:47:19
Done.
| |
| 315 } | |
| 316 } | |
| 317 | |
| 318 | |
| 319 void GCIdleTimeHandler::ResetCounters() { | |
| 320 long_idle_notifications_ = 0; | |
| 321 idle_mark_compacts_ = 0; | |
| 322 mark_compacts_ = 0; | |
| 323 scavenges_ = 0; | |
| 324 } | |
| 325 | |
| 326 | |
| 327 bool GCIdleTimeHandler::IsMutatorActive(int contexts_disposed, int gcs) { | |
| 328 return contexts_disposed > 0 || gcs >= kGCsBeforeMutatorIsActive; | |
| 329 } | |
| 330 | |
| 331 | |
| 332 bool GCIdleTimeHandler::IsMutatorIdle(int long_idle_notifications, int gcs) { | |
| 333 return gcs == 0 && | |
| 334 long_idle_notifications >= kLongIdleNotificationsBeforeMutatorIsIdle; | |
| 335 } | |
| 336 | |
| 337 | |
| 338 GCIdleTimeHandler::Mode GCIdleTimeHandler::NextMode( | |
| 339 const HeapState& heap_state) { | |
| 340 DCHECK(mark_compacts_ >= idle_mark_compacts_); | |
| 341 int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; | |
| 342 switch (mode_) { | |
| 343 case kDone: | |
| 344 DCHECK(idle_mark_compacts_ == 0); | |
| 345 if (IsMutatorActive(heap_state.contexts_disposed, mutator_gcs)) { | |
| 346 return kReduceLatency; | |
| 347 } | |
| 348 break; | |
| 349 case kReduceLatency: | |
| 350 if (IsMutatorIdle(long_idle_notifications_, mutator_gcs)) { | |
| 351 return kReduceMemory; | |
| 352 } | |
| 353 break; | |
| 354 case kReduceMemory: | |
| 355 if (idle_mark_compacts_ >= kMaxIdleMarkCompacts) { | |
| 356 return kDone; | |
| 357 } | |
| 358 if (mutator_gcs > idle_mark_compacts_) { | |
| 359 return kReduceLatency; | |
| 360 } | |
| 361 break; | |
| 362 } | |
| 363 return mode_; | |
| 273 } | 364 } |
| 274 } | 365 } |
| 275 } | 366 } |
| OLD | NEW |