Commit b4cfd60e authored by ulan's avatar ulan Committed by Commit bot

Ensure the memory reduces makes progress.

BUG=

Review URL: https://codereview.chromium.org/1262363002

Cr-Commit-Position: refs/heads/master@{#29950}
parent ec9bc794
......@@ -14,6 +14,7 @@ namespace internal {
const int MemoryReducer::kLongDelayMs = 5000;
const int MemoryReducer::kShortDelayMs = 500;
const int MemoryReducer::kWatchdogDelayMs = 100000;
const int MemoryReducer::kMaxNumberOfGCs = 3;
MemoryReducer::TimerTask::TimerTask(MemoryReducer* memory_reducer)
......@@ -106,11 +107,17 @@ void MemoryReducer::NotifyBackgroundIdleNotification(const Event& event) {
}
bool MemoryReducer::WatchdogGC(const State& state, const Event& event) {
return state.last_gc_time_ms != 0 &&
event.time_ms > state.last_gc_time_ms + kWatchdogDelayMs;
}
// For specification of this function see the comment for MemoryReducer class.
MemoryReducer::State MemoryReducer::Step(const State& state,
const Event& event) {
if (!FLAG_incremental_marking) {
return State(kDone, 0, 0);
return State(kDone, 0, 0, state.last_gc_time_ms);
}
switch (state.action) {
case kDone:
......@@ -118,7 +125,9 @@ MemoryReducer::State MemoryReducer::Step(const State& state,
return state;
} else {
DCHECK(event.type == kContextDisposed || event.type == kMarkCompact);
return State(kWait, 0, event.time_ms + kLongDelayMs);
return State(
kWait, 0, event.time_ms + kLongDelayMs,
event.type == kMarkCompact ? event.time_ms : state.last_gc_time_ms);
}
case kWait:
switch (event.type) {
......@@ -126,28 +135,30 @@ MemoryReducer::State MemoryReducer::Step(const State& state,
return state;
case kTimer:
if (state.started_gcs >= kMaxNumberOfGCs) {
return State(kDone, 0, 0.0);
return State(kDone, 0, 0.0, state.last_gc_time_ms);
} else if (event.can_start_incremental_gc &&
event.low_allocation_rate) {
(event.low_allocation_rate || WatchdogGC(state, event))) {
if (state.next_gc_start_ms <= event.time_ms) {
return State(kRun, state.started_gcs + 1, 0.0);
return State(kRun, state.started_gcs + 1, 0.0,
state.last_gc_time_ms);
} else {
return state;
}
} else {
return State(kWait, state.started_gcs,
event.time_ms + kLongDelayMs);
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs,
state.last_gc_time_ms);
}
case kBackgroundIdleNotification:
if (event.can_start_incremental_gc &&
state.started_gcs < kMaxNumberOfGCs) {
return State(kWait, state.started_gcs + 1,
event.time_ms + kLongDelayMs);
event.time_ms + kLongDelayMs, state.last_gc_time_ms);
} else {
return state;
}
case kMarkCompact:
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs);
return State(kWait, state.started_gcs, event.time_ms + kLongDelayMs,
event.time_ms);
}
case kRun:
if (event.type != kMarkCompact) {
......@@ -155,14 +166,15 @@ MemoryReducer::State MemoryReducer::Step(const State& state,
} else {
if (state.started_gcs < kMaxNumberOfGCs &&
(event.next_gc_likely_to_collect_more || state.started_gcs == 1)) {
return State(kWait, state.started_gcs, event.time_ms + kShortDelayMs);
return State(kWait, state.started_gcs, event.time_ms + kShortDelayMs,
event.time_ms);
} else {
return State(kDone, 0, 0.0);
return State(kDone, 0, 0.0, event.time_ms);
}
}
}
UNREACHABLE();
return State(kDone, 0, 0); // Make the compiler happy.
return State(kDone, 0, 0, 0.0); // Make the compiler happy.
}
......@@ -177,9 +189,7 @@ void MemoryReducer::ScheduleTimer(double delay_ms) {
}
void MemoryReducer::TearDown() {
state_ = State(kDone, 0, 0);
}
void MemoryReducer::TearDown() { state_ = State(kDone, 0, 0, 0.0); }
} // internal
} // v8
......@@ -22,69 +22,78 @@ class Heap;
// The class implements an automaton with the following states and transitions.
//
// States:
// - DONE
// - WAIT <started_gcs> <next_gc_start_ms>
// - RUN <started_gcs>
// - DONE <last_gc_time_ms>
// - WAIT <started_gcs> <next_gc_start_ms> <last_gc_time_ms>
// - RUN <started_gcs> <last_gc_time_ms>
// The <started_gcs> is an integer in range from 0..kMaxNumberOfGCs that stores
// the number of GCs initiated by the MemoryReducer since it left the DONE
// state.
// The <next_gc_start_ms> is a double that stores the earliest time the next GC
// can be initiated by the MemoryReducer.
// The <last_gc_start_ms> is a double that stores the time of the last full GC.
// The DONE state means that the MemoryReducer is not active.
// The WAIT state means that the MemoryReducer is waiting for mutator allocation
// rate to drop. The check for the allocation rate happens in the timer task
// callback.
// callback. If the allocation rate does not drop in watchdog_delay_ms since
// the last GC then transition to the RUN state is forced.
// The RUN state means that the MemoryReducer started incremental marking and is
// waiting for it to finish. Incremental marking steps are performed as usual
// in the idle notification and in the mutator.
//
// Transitions:
// DONE -> WAIT 0 (now_ms + long_delay_ms) happens:
// - on context disposal,
// DONE t -> WAIT 0 (now_ms + long_delay_ms) t' happens:
// - on context disposal.
// - at the end of mark-compact GC initiated by the mutator.
// This signals that there is potential garbage to be collected.
//
// WAIT n x -> WAIT n (now_ms + long_delay_ms) happens:
// WAIT n x t -> WAIT n (now_ms + long_delay_ms) t' happens:
// - on mark-compact GC initiated by the mutator,
// - in the timer callback if the mutator allocation rate is high or
// incremental GC is in progress.
// incremental GC is in progress or (now_ms - t < watchdog_delay_ms)
//
// WAIT n x -> WAIT (n+1) happens:
// WAIT n x t -> WAIT (n+1) t happens:
// - on background idle notification, which signals that we can start
// incremental marking even if the allocation rate is high.
// The MemoryReducer starts incremental marking on this transition but still
// has a pending timer task.
//
// WAIT n x -> DONE happens:
// WAIT n x t -> DONE t happens:
// - in the timer callback if n >= kMaxNumberOfGCs.
//
// WAIT n x -> RUN (n+1) happens:
// WAIT n x t -> RUN (n+1) t happens:
// - in the timer callback if the mutator allocation rate is low
// and now_ms >= x and there is no incremental GC in progress.
// - in the timer callback if (now_ms - t > watchdog_delay_ms) and
// and now_ms >= x and there is no incremental GC in progress.
// The MemoryReducer starts incremental marking on this transition.
//
// RUN n -> DONE happens:
// RUN n t -> DONE now_ms happens:
// - at end of the incremental GC initiated by the MemoryReducer if
// (n > 1 and there is no more garbage to be collected) or
// n == kMaxNumberOfGCs.
// RUN n -> WAIT n (now_ms + short_delay_ms) happens:
// RUN n t -> WAIT n (now_ms + short_delay_ms) now_ms happens:
// - at end of the incremental GC initiated by the MemoryReducer if
// (n == 1 or there is more garbage to be collected) and
// n < kMaxNumberOfGCs.
//
// now_ms is the current time, long_delay_ms and short_delay_ms are constants.
// now_ms is the current time,
// t' is t if the current event is not a GC event and is now_ms otherwise,
// long_delay_ms, short_delay_ms, and watchdog_delay_ms are constants.
class MemoryReducer {
public:
enum Action { kDone, kWait, kRun };
struct State {
State(Action action, int started_gcs, double next_gc_start_ms)
State(Action action, int started_gcs, double next_gc_start_ms,
double last_gc_time_ms)
: action(action),
started_gcs(started_gcs),
next_gc_start_ms(next_gc_start_ms) {}
next_gc_start_ms(next_gc_start_ms),
last_gc_time_ms(last_gc_time_ms) {}
Action action;
int started_gcs;
double next_gc_start_ms;
double last_gc_time_ms;
};
enum EventType {
......@@ -102,7 +111,8 @@ class MemoryReducer {
bool can_start_incremental_gc;
};
explicit MemoryReducer(Heap* heap) : heap_(heap), state_(kDone, 0, 0.0) {}
explicit MemoryReducer(Heap* heap)
: heap_(heap), state_(kDone, 0, 0.0, 0.0) {}
// Callbacks.
void NotifyMarkCompact(const Event& event);
void NotifyContextDisposed(const Event& event);
......@@ -115,6 +125,7 @@ class MemoryReducer {
void TearDown();
static const int kLongDelayMs;
static const int kShortDelayMs;
static const int kWatchdogDelayMs;
static const int kMaxNumberOfGCs;
Heap* heap() { return heap_; }
......@@ -133,9 +144,10 @@ class MemoryReducer {
void NotifyTimer(const Event& event);
static bool WatchdogGC(const State& state, const Event& event);
Heap* heap_;
State state_;
DISALLOW_COPY_AND_ASSIGN(MemoryReducer);
};
......
......@@ -12,19 +12,19 @@ namespace v8 {
namespace internal {
MemoryReducer::State DoneState() {
return MemoryReducer::State(MemoryReducer::kDone, 0, 0.0);
return MemoryReducer::State(MemoryReducer::kDone, 0, 0.0, 1.0);
}
MemoryReducer::State WaitState(int started_gcs, double next_gc_start_ms) {
return MemoryReducer::State(MemoryReducer::kWait, started_gcs,
next_gc_start_ms);
next_gc_start_ms, 1.0);
}
MemoryReducer::State RunState(int started_gcs, double next_gc_start_ms) {
return MemoryReducer::State(MemoryReducer::kRun, started_gcs,
next_gc_start_ms);
next_gc_start_ms, 1.0);
}
......@@ -114,20 +114,23 @@ TEST(MemoryReducer, FromDoneToWait) {
MemoryReducer::State state0(DoneState()), state1(DoneState());
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(0));
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(2));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kLongDelayMs + 2, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(0));
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(2));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(MemoryReducer::kLongDelayMs + 2, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, ContextDisposedEvent(0));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
}
......@@ -161,11 +164,13 @@ TEST(MemoryReducer, FromWaitToWait) {
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, MarkCompactEventNoGarbageLeft(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
......@@ -178,6 +183,23 @@ TEST(MemoryReducer, FromWaitToWait) {
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
state0.last_gc_time_ms = 0;
state1 = MemoryReducer::Step(
state0,
TimerEventHighAllocationRate(MemoryReducer::kWatchdogDelayMs + 1));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(MemoryReducer::kWatchdogDelayMs + 1 + MemoryReducer::kLongDelayMs,
state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state0.last_gc_time_ms = 1;
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kLongDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
state1 = MemoryReducer::Step(state0, BackgroundIdleNotificationEvent(2000));
EXPECT_EQ(MemoryReducer::kWait, state1.action);
......@@ -196,6 +218,14 @@ TEST(MemoryReducer, FromWaitToRun) {
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs + 1, state1.started_gcs);
state1 = MemoryReducer::Step(
state0,
TimerEventHighAllocationRate(MemoryReducer::kWatchdogDelayMs + 2));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs + 1, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
}
......@@ -210,16 +240,19 @@ TEST(MemoryReducer, FromWaitToDone) {
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
}
......@@ -232,21 +265,25 @@ TEST(MemoryReducer, FromRunToRun) {
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, TimerEventHighAllocationRate(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, TimerEventPendingGC(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
state1 = MemoryReducer::Step(state0, ContextDisposedEvent(2000));
EXPECT_EQ(MemoryReducer::kRun, state1.action);
EXPECT_EQ(state0.next_gc_start_ms, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(state0.last_gc_time_ms, state1.last_gc_time_ms);
}
......@@ -259,13 +296,14 @@ TEST(MemoryReducer, FromRunToDone) {
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
state0.started_gcs = MemoryReducer::kMaxNumberOfGCs;
state1 = MemoryReducer::Step(state0, MarkCompactEventGarbageLeft(2000));
EXPECT_EQ(MemoryReducer::kDone, state1.action);
EXPECT_EQ(0, state1.next_gc_start_ms);
EXPECT_EQ(0, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
}
......@@ -278,6 +316,7 @@ TEST(MemoryReducer, FromRunToWait) {
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
state0.started_gcs = 1;
......@@ -285,6 +324,7 @@ TEST(MemoryReducer, FromRunToWait) {
EXPECT_EQ(MemoryReducer::kWait, state1.action);
EXPECT_EQ(2000 + MemoryReducer::kShortDelayMs, state1.next_gc_start_ms);
EXPECT_EQ(state0.started_gcs, state1.started_gcs);
EXPECT_EQ(2000, state1.last_gc_time_ms);
}
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment