Index: src/heap/gc-idle-time-handler.cc |
diff --git a/src/heap/gc-idle-time-handler.cc b/src/heap/gc-idle-time-handler.cc |
index a3383b9c0c0dcde34864a7fbbafef853d24d5d55..3e4df7d1c848e7cb1f7092dcffe0c7f53c9bb2e6 100644 |
--- a/src/heap/gc-idle-time-handler.cc |
+++ b/src/heap/gc-idle-time-handler.cc |
@@ -12,8 +12,6 @@ namespace internal { |
const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9; |
const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000; |
const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000; |
-const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 2; |
-const int GCIdleTimeHandler::kIdleScavengeThreshold = 5; |
const double GCIdleTimeHandler::kHighContextDisposalRate = 100; |
const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1; |
@@ -187,13 +185,56 @@ bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure( |
} |
-GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { |
- if (idle_times_which_made_no_progress_since_last_idle_round_ >= |
- kMaxNoProgressIdleTimesPerIdleRound) { |
+// The idle time handler has three modes and transitions between them |
+// as shown in the diagram: |
+// |
+// kReduceLatency -----> kReduceMemory -----> kDone |
+// ^ ^ | | |
+// | | | | |
+// | +------------------+ | |
+// | | |
+// +----------------------------------------+ |
+// |
+// In kReduceLatency mode the handler only starts incremental marking |
+// if can_start_incremental_marking is false. |
+// In kReduceMemory mode the handler can force a new GC cycle by starting |
+// incremental marking even if can_start_incremental_marking is false. It can |
+// cause at most X idle GCs. |
+// In kDone mode the idle time handler does nothing. |
+// |
+// The initial mode is kReduceLatency. |
+// |
+// kReduceLatency => kReduceMemory transition happens if there were Y |
+// consecutive long idle notifications without any mutator GC. This is our |
+// notion of "mutator is idle". |
+// |
+// kReduceMemory => kDone transition happens after X idle GCs. |
+// |
+// kReduceMemory => kReduceLatency transition happens if N mutator GCs |
+// were performed meaning that the mutator is active. |
+// |
+// kDone => kReduceLatency transition happens if there were M mutator GCs or |
+// context was disposed. |
+// |
+// X = kMaxIdleMarkCompacts |
+// Y = kLongIdleNotificationsBeforeMutatorIsIdle |
+// N = #(idle GCs) |
+// M = kGCsBeforeMutatorIsActive |
+GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, |
+ HeapState heap_state) { |
+ Mode next_mode = NextMode(heap_state); |
+ |
+ if (next_mode != mode_) { |
+ mode_ = next_mode; |
+ ResetCounters(); |
+ } |
+ |
+ UpdateCounters(idle_time_in_ms); |
+ |
+ if (mode_ == kDone) { |
return GCIdleTimeAction::Done(); |
} else { |
- idle_times_which_made_no_progress_since_last_idle_round_++; |
- return GCIdleTimeAction::Nothing(); |
+ return Action(idle_time_in_ms, heap_state, mode_ == kReduceMemory); |
} |
} |
@@ -204,28 +245,23 @@ GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() { |
// a full GC. |
// (2) If the new space is almost full and we can afford a Scavenge or if the |
// next Scavenge will very likely take long, then a Scavenge is performed. |
-// (3) If there is currently no MarkCompact idle round going on, we start a |
-// new idle round if enough garbage was created. Otherwise we do not perform |
-// garbage collection to keep system utilization low. |
-// (4) If incremental marking is done, we perform a full garbage collection |
+// (3) If incremental marking is done, we perform a full garbage collection |
// if we are allowed to still do full garbage collections during this idle |
// round or if we are not allowed to start incremental marking. Otherwise we |
// do not perform garbage collection to keep system utilization low. |
-// (5) If sweeping is in progress and we received a large enough idle time |
+// (4) If sweeping is in progress and we received a large enough idle time |
// request, we finalize sweeping here. |
-// (6) If incremental marking is in progress, we perform a marking step. Note, |
+// (5) If incremental marking is in progress, we perform a marking step. Note, |
// that this currently may trigger a full garbage collection. |
-GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, |
- HeapState heap_state) { |
+GCIdleTimeAction GCIdleTimeHandler::Action(double idle_time_in_ms, |
+ const HeapState& heap_state, |
+ bool reduce_memory) { |
if (static_cast<int>(idle_time_in_ms) <= 0) { |
- if (heap_state.contexts_disposed > 0) { |
- StartIdleRound(); |
- } |
if (heap_state.incremental_marking_stopped) { |
if (ShouldDoContextDisposalMarkCompact( |
heap_state.contexts_disposed, |
heap_state.contexts_disposal_rate)) { |
- return GCIdleTimeAction::FullGC(); |
+ return GCIdleTimeAction::FullGC(false); |
} |
} |
return GCIdleTimeAction::Nothing(); |
@@ -239,37 +275,99 @@ GCIdleTimeAction GCIdleTimeHandler::Compute(double idle_time_in_ms, |
return GCIdleTimeAction::Scavenge(); |
} |
- if (IsMarkCompactIdleRoundFinished()) { |
- if (EnoughGarbageSinceLastIdleRound()) { |
- StartIdleRound(); |
- } else { |
- return GCIdleTimeAction::Done(); |
- } |
- } |
- |
- if (heap_state.incremental_marking_stopped) { |
+ if (heap_state.incremental_marking_stopped && reduce_memory) { |
if (ShouldDoMarkCompact(static_cast<size_t>(idle_time_in_ms), |
heap_state.size_of_objects, |
heap_state.mark_compact_speed_in_bytes_per_ms)) { |
- return GCIdleTimeAction::FullGC(); |
+ return GCIdleTimeAction::FullGC(reduce_memory); |
} |
} |
+ |
if (heap_state.sweeping_in_progress) { |
if (heap_state.sweeping_completed) { |
return GCIdleTimeAction::FinalizeSweeping(); |
} else { |
- return NothingOrDone(); |
+ return GCIdleTimeAction::Nothing(); |
} |
} |
+ |
if (heap_state.incremental_marking_stopped && |
- !heap_state.can_start_incremental_marking) { |
- return NothingOrDone(); |
+ !heap_state.can_start_incremental_marking && !reduce_memory) { |
+ return GCIdleTimeAction::Nothing(); |
} |
size_t step_size = EstimateMarkingStepSize( |
static_cast<size_t>(kIncrementalMarkingStepTimeInMs), |
heap_state.incremental_marking_speed_in_bytes_per_ms); |
- return GCIdleTimeAction::IncrementalMarking(step_size); |
+ return GCIdleTimeAction::IncrementalMarking(step_size, reduce_memory); |
+} |
+ |
+ |
+void GCIdleTimeHandler::UpdateCounters(double idle_time_in_ms) { |
+ if (mode_ == kReduceLatency) { |
+ int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; |
+ if (mutator_gcs > 0) { |
+ // There was a mutator GC since the last notification. |
+ long_idle_notifications_ = 0; |
+ } |
+ idle_mark_compacts_ = 0; |
+ mark_compacts_ = 0; |
+ scavenges_ = 0; |
+ if (idle_time_in_ms >= kMinLongIdleTime) { |
+ long_idle_notifications_ += |
+ (idle_time_in_ms >= kLargeLongIdleTime) |
+ ? kLongIdleNotificationsBeforeMutatorIsIdle |
+ : 1; |
+ } |
+ } |
+} |
+ |
+ |
+void GCIdleTimeHandler::ResetCounters() { |
+ long_idle_notifications_ = 0; |
+ idle_mark_compacts_ = 0; |
+ mark_compacts_ = 0; |
+ scavenges_ = 0; |
+} |
+ |
+ |
+bool GCIdleTimeHandler::IsMutatorActive(int contexts_disposed, int gcs) { |
+ return contexts_disposed > 0 || gcs >= kGCsBeforeMutatorIsActive; |
+} |
+ |
+ |
+bool GCIdleTimeHandler::IsMutatorIdle(int long_idle_notifications, int gcs) { |
+ return gcs == 0 && |
+ long_idle_notifications >= kLongIdleNotificationsBeforeMutatorIsIdle; |
+} |
+ |
+ |
+GCIdleTimeHandler::Mode GCIdleTimeHandler::NextMode( |
+ const HeapState& heap_state) { |
+ DCHECK(mark_compacts_ >= idle_mark_compacts_); |
+ int mutator_gcs = scavenges_ + mark_compacts_ - idle_mark_compacts_; |
+ switch (mode_) { |
+ case kDone: |
+ DCHECK(idle_mark_compacts_ == 0); |
+ if (IsMutatorActive(heap_state.contexts_disposed, mutator_gcs)) { |
+ return kReduceLatency; |
+ } |
+ break; |
+ case kReduceLatency: |
+ if (IsMutatorIdle(long_idle_notifications_, mutator_gcs)) { |
+ return kReduceMemory; |
+ } |
+ break; |
+ case kReduceMemory: |
+ if (idle_mark_compacts_ >= kMaxIdleMarkCompacts) { |
+ return kDone; |
+ } |
+ if (mutator_gcs > idle_mark_compacts_) { |
+ return kReduceLatency; |
+ } |
+ break; |
+ } |
+ return mode_; |
} |
} |
} |