aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/heap/gc-idle-time-handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/heap/gc-idle-time-handler.cc')
-rw-r--r--deps/v8/src/heap/gc-idle-time-handler.cc190
1 files changed, 155 insertions, 35 deletions
diff --git a/deps/v8/src/heap/gc-idle-time-handler.cc b/deps/v8/src/heap/gc-idle-time-handler.cc
index 5060bc90d1..e20a1e45cb 100644
--- a/deps/v8/src/heap/gc-idle-time-handler.cc
+++ b/deps/v8/src/heap/gc-idle-time-handler.cc
@@ -12,9 +12,6 @@ namespace internal {
const double GCIdleTimeHandler::kConservativeTimeRatio = 0.9;
const size_t GCIdleTimeHandler::kMaxMarkCompactTimeInMs = 1000;
const size_t GCIdleTimeHandler::kMaxFinalIncrementalMarkCompactTimeInMs = 1000;
-const size_t GCIdleTimeHandler::kMinTimeForFinalizeSweeping = 100;
-const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 2;
-const int GCIdleTimeHandler::kIdleScavengeThreshold = 5;
const double GCIdleTimeHandler::kHighContextDisposalRate = 100;
const size_t GCIdleTimeHandler::kMinTimeForOverApproximatingWeakClosureInMs = 1;
@@ -132,8 +129,12 @@ bool GCIdleTimeHandler::ShouldDoScavenge(
static_cast<size_t>(new_space_size * kConservativeTimeRatio);
} else {
// We have to trigger scavenge before we reach the end of new space.
- new_space_allocation_limit -=
- new_space_allocation_throughput_in_bytes_per_ms * kMaxScheduledIdleTime;
+ size_t adjust_limit = new_space_allocation_throughput_in_bytes_per_ms *
+ kTimeUntilNextIdleEvent;
+ if (adjust_limit > new_space_allocation_limit)
+ new_space_allocation_limit = 0;
+ else
+ new_space_allocation_limit -= adjust_limit;
}
if (scavenge_speed_in_bytes_per_ms == 0) {
@@ -185,44 +186,93 @@ bool GCIdleTimeHandler::ShouldDoOverApproximateWeakClosure(
GCIdleTimeAction GCIdleTimeHandler::NothingOrDone() {
- if (idle_times_which_made_no_progress_since_last_idle_round_ >=
- kMaxNoProgressIdleTimesPerIdleRound) {
+ if (idle_times_which_made_no_progress_per_mode_ >=
+ kMaxNoProgressIdleTimesPerMode) {
return GCIdleTimeAction::Done();
} else {
- idle_times_which_made_no_progress_since_last_idle_round_++;
+ idle_times_which_made_no_progress_per_mode_++;
return GCIdleTimeAction::Nothing();
}
}
+// 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 {
+ return Action(idle_time_in_ms, heap_state, mode_ == kReduceMemory);
+ }
+}
+
+
// The following logic is implemented by the controller:
// (1) If we don't have any idle time, do nothing, unless a context was
// disposed, incremental marking is stopped, and the heap is small. Then do
// 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();
@@ -236,39 +286,109 @@ 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);
}
}
- // TODO(hpayer): Estimate finalize sweeping time.
if (heap_state.sweeping_in_progress) {
- if (static_cast<size_t>(idle_time_in_ms) >= kMinTimeForFinalizeSweeping) {
+ if (heap_state.sweeping_completed) {
return GCIdleTimeAction::FinalizeSweeping();
+ } else {
+ return NothingOrDone();
}
- return NothingOrDone();
}
if (heap_state.incremental_marking_stopped &&
- !heap_state.can_start_incremental_marking) {
+ !heap_state.can_start_incremental_marking && !reduce_memory) {
return NothingOrDone();
}
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 gcs = scavenges_ + mark_compacts_;
+ if (gcs > 0) {
+ // There was a GC since the last notification.
+ long_idle_notifications_ = 0;
+ background_idle_notifications_ = 0;
+ }
+ idle_mark_compacts_ = 0;
+ mark_compacts_ = 0;
+ scavenges_ = 0;
+ if (idle_time_in_ms >= kMinBackgroundIdleTime) {
+ background_idle_notifications_++;
+ } else if (idle_time_in_ms >= kMinLongIdleTime) {
+ long_idle_notifications_++;
+ }
+ }
+}
+
+
+void GCIdleTimeHandler::ResetCounters() {
+ long_idle_notifications_ = 0;
+ background_idle_notifications_ = 0;
+ idle_mark_compacts_ = 0;
+ mark_compacts_ = 0;
+ scavenges_ = 0;
+ idle_times_which_made_no_progress_per_mode_ = 0;
+}
+
+
+bool GCIdleTimeHandler::IsMutatorActive(int contexts_disposed,
+ int mark_compacts) {
+ return contexts_disposed > 0 ||
+ mark_compacts >= kMarkCompactsBeforeMutatorIsActive;
+}
+
+
+bool GCIdleTimeHandler::IsMutatorIdle(int long_idle_notifications,
+ int background_idle_notifications,
+ int mutator_gcs) {
+ return mutator_gcs == 0 &&
+ (long_idle_notifications >=
+ kLongIdleNotificationsBeforeMutatorIsIdle ||
+ background_idle_notifications >=
+ kBackgroundIdleNotificationsBeforeMutatorIsIdle);
+}
+
+
+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, mark_compacts_)) {
+ return kReduceLatency;
+ }
+ break;
+ case kReduceLatency:
+ if (IsMutatorIdle(long_idle_notifications_,
+ background_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_;
}
}
}