| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_HEAP_MEMORY_REDUCER_H_ |
| #define V8_HEAP_MEMORY_REDUCER_H_ |
| |
| #include "include/v8-platform.h" |
| #include "src/base/macros.h" |
| #include "src/common/globals.h" |
| #include "src/tasks/cancelable-task.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace heap { |
| class HeapTester; |
| } // namespace heap |
| |
| class Heap; |
| |
| |
| // The goal of the MemoryReducer class is to detect transition of the mutator |
| // from high allocation phase to low allocation phase and to collect potential |
| // garbage created in the high allocation phase. |
| // |
| // The class implements an automaton with the following states and transitions. |
| // |
| // States: |
| // - 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. 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 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 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 or (now_ms - t < watchdog_delay_ms) |
| // |
| // 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 t -> DONE t happens: |
| // - in the timer callback if n >= kMaxNumberOfGCs. |
| // |
| // 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 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 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, |
| // 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 V8_EXPORT_PRIVATE MemoryReducer { |
| public: |
| enum Action { kDone, kWait, kRun }; |
| |
| struct State { |
| State(Action action, int started_gcs, double next_gc_start_ms, |
| double last_gc_time_ms, size_t committed_memory_at_last_run) |
| : action(action), |
| started_gcs(started_gcs), |
| next_gc_start_ms(next_gc_start_ms), |
| last_gc_time_ms(last_gc_time_ms), |
| committed_memory_at_last_run(committed_memory_at_last_run) {} |
| Action action; |
| int started_gcs; |
| double next_gc_start_ms; |
| double last_gc_time_ms; |
| size_t committed_memory_at_last_run; |
| }; |
| |
| enum EventType { kTimer, kMarkCompact, kPossibleGarbage }; |
| |
| struct Event { |
| EventType type; |
| double time_ms; |
| size_t committed_memory; |
| bool next_gc_likely_to_collect_more; |
| bool should_start_incremental_gc; |
| bool can_start_incremental_gc; |
| }; |
| |
| explicit MemoryReducer(Heap* heap); |
| // Callbacks. |
| void NotifyMarkCompact(const Event& event); |
| void NotifyPossibleGarbage(const Event& event); |
| void NotifyBackgroundIdleNotification(const Event& event); |
| // The step function that computes the next state from the current state and |
| // the incoming event. |
| static State Step(const State& state, const Event& event); |
| // Posts a timer task that will call NotifyTimer after the given delay. |
| void ScheduleTimer(double delay_ms); |
| void TearDown(); |
| static const int kLongDelayMs; |
| static const int kShortDelayMs; |
| static const int kWatchdogDelayMs; |
| static const int kMaxNumberOfGCs; |
| // The committed memory has to increase by at least this factor since the |
| // last run in order to trigger a new run after mark-compact. |
| static const double kCommittedMemoryFactor; |
| // The committed memory has to increase by at least this amount since the |
| // last run in order to trigger a new run after mark-compact. |
| static const size_t kCommittedMemoryDelta; |
| |
| Heap* heap() { return heap_; } |
| |
| bool ShouldGrowHeapSlowly() { |
| return state_.action == kDone && state_.started_gcs > 0; |
| } |
| |
| private: |
| class TimerTask : public v8::internal::CancelableTask { |
| public: |
| explicit TimerTask(MemoryReducer* memory_reducer); |
| |
| private: |
| // v8::internal::CancelableTask overrides. |
| void RunInternal() override; |
| MemoryReducer* memory_reducer_; |
| DISALLOW_COPY_AND_ASSIGN(TimerTask); |
| }; |
| |
| void NotifyTimer(const Event& event); |
| |
| static bool WatchdogGC(const State& state, const Event& event); |
| |
| Heap* heap_; |
| std::shared_ptr<v8::TaskRunner> taskrunner_; |
| State state_; |
| unsigned int js_calls_counter_; |
| double js_calls_sample_time_ms_; |
| |
| // Used in cctest. |
| friend class heap::HeapTester; |
| DISALLOW_COPY_AND_ASSIGN(MemoryReducer); |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_HEAP_MEMORY_REDUCER_H_ |