• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_HEAP_MEMORY_REDUCER_H_
6 #define V8_HEAP_MEMORY_REDUCER_H_
7 
8 #include "include/v8-platform.h"
9 #include "src/base/macros.h"
10 #include "src/common/globals.h"
11 #include "src/tasks/cancelable-task.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 namespace heap {
17 class HeapTester;
18 }  // namespace heap
19 
20 class Heap;
21 
22 
23 // The goal of the MemoryReducer class is to detect transition of the mutator
24 // from high allocation phase to low allocation phase and to collect potential
25 // garbage created in the high allocation phase.
26 //
27 // The class implements an automaton with the following states and transitions.
28 //
29 // States:
30 // - DONE <last_gc_time_ms>
31 // - WAIT <started_gcs> <next_gc_start_ms> <last_gc_time_ms>
32 // - RUN <started_gcs> <last_gc_time_ms>
33 // The <started_gcs> is an integer in range from 0..kMaxNumberOfGCs that stores
34 // the number of GCs initiated by the MemoryReducer since it left the DONE
35 // state.
36 // The <next_gc_start_ms> is a double that stores the earliest time the next GC
37 // can be initiated by the MemoryReducer.
38 // The <last_gc_start_ms> is a double that stores the time of the last full GC.
39 // The DONE state means that the MemoryReducer is not active.
40 // The WAIT state means that the MemoryReducer is waiting for mutator allocation
41 // rate to drop. The check for the allocation rate happens in the timer task
42 // callback. If the allocation rate does not drop in watchdog_delay_ms since
43 // the last GC then transition to the RUN state is forced.
44 // The RUN state means that the MemoryReducer started incremental marking and is
45 // waiting for it to finish. Incremental marking steps are performed as usual
46 // in the idle notification and in the mutator.
47 //
48 // Transitions:
49 // DONE t -> WAIT 0 (now_ms + long_delay_ms) t' happens:
50 //     - on context disposal.
51 //     - at the end of mark-compact GC initiated by the mutator.
52 // This signals that there is potential garbage to be collected.
53 //
54 // WAIT n x t -> WAIT n (now_ms + long_delay_ms) t' happens:
55 //     - on mark-compact GC initiated by the mutator,
56 //     - in the timer callback if the mutator allocation rate is high or
57 //       incremental GC is in progress or (now_ms - t < watchdog_delay_ms)
58 //
59 // WAIT n x t -> WAIT (n+1) t happens:
60 //     - on background idle notification, which signals that we can start
61 //       incremental marking even if the allocation rate is high.
62 // The MemoryReducer starts incremental marking on this transition but still
63 // has a pending timer task.
64 //
65 // WAIT n x t -> DONE t happens:
66 //     - in the timer callback if n >= kMaxNumberOfGCs.
67 //
68 // WAIT n x t -> RUN (n+1) t happens:
69 //     - in the timer callback if the mutator allocation rate is low
70 //       and now_ms >= x and there is no incremental GC in progress.
71 //     - in the timer callback if (now_ms - t > watchdog_delay_ms) and
72 //       and now_ms >= x and there is no incremental GC in progress.
73 // The MemoryReducer starts incremental marking on this transition.
74 //
75 // RUN n t -> DONE now_ms happens:
76 //     - at end of the incremental GC initiated by the MemoryReducer if
77 //       (n > 1 and there is no more garbage to be collected) or
78 //       n == kMaxNumberOfGCs.
79 // RUN n t -> WAIT n (now_ms + short_delay_ms) now_ms happens:
80 //     - at end of the incremental GC initiated by the MemoryReducer if
81 //       (n == 1 or there is more garbage to be collected) and
82 //       n < kMaxNumberOfGCs.
83 //
84 // now_ms is the current time,
85 // t' is t if the current event is not a GC event and is now_ms otherwise,
86 // long_delay_ms, short_delay_ms, and watchdog_delay_ms are constants.
87 class V8_EXPORT_PRIVATE MemoryReducer {
88  public:
89   enum Action { kDone, kWait, kRun };
90 
91   struct State {
StateState92     State(Action action, int started_gcs, double next_gc_start_ms,
93           double last_gc_time_ms, size_t committed_memory_at_last_run)
94         : action(action),
95           started_gcs(started_gcs),
96           next_gc_start_ms(next_gc_start_ms),
97           last_gc_time_ms(last_gc_time_ms),
98           committed_memory_at_last_run(committed_memory_at_last_run) {}
99     Action action;
100     int started_gcs;
101     double next_gc_start_ms;
102     double last_gc_time_ms;
103     size_t committed_memory_at_last_run;
104   };
105 
106   enum EventType { kTimer, kMarkCompact, kPossibleGarbage };
107 
108   struct Event {
109     EventType type;
110     double time_ms;
111     size_t committed_memory;
112     bool next_gc_likely_to_collect_more;
113     bool should_start_incremental_gc;
114     bool can_start_incremental_gc;
115   };
116 
117   explicit MemoryReducer(Heap* heap);
118   MemoryReducer(const MemoryReducer&) = delete;
119   MemoryReducer& operator=(const MemoryReducer&) = delete;
120   // Callbacks.
121   void NotifyMarkCompact(const Event& event);
122   void NotifyPossibleGarbage(const Event& event);
123   void NotifyBackgroundIdleNotification(const Event& event);
124   // The step function that computes the next state from the current state and
125   // the incoming event.
126   static State Step(const State& state, const Event& event);
127   // Posts a timer task that will call NotifyTimer after the given delay.
128   void ScheduleTimer(double delay_ms);
129   void TearDown();
130   static const int kLongDelayMs;
131   static const int kShortDelayMs;
132   static const int kWatchdogDelayMs;
133   static const int kMaxNumberOfGCs;
134   // The committed memory has to increase by at least this factor since the
135   // last run in order to trigger a new run after mark-compact.
136   static const double kCommittedMemoryFactor;
137   // The committed memory has to increase by at least this amount since the
138   // last run in order to trigger a new run after mark-compact.
139   static const size_t kCommittedMemoryDelta;
140 
heap()141   Heap* heap() { return heap_; }
142 
ShouldGrowHeapSlowly()143   bool ShouldGrowHeapSlowly() {
144     return state_.action == kDone && state_.started_gcs > 0;
145   }
146 
147  private:
148   class TimerTask : public v8::internal::CancelableTask {
149    public:
150     explicit TimerTask(MemoryReducer* memory_reducer);
151     TimerTask(const TimerTask&) = delete;
152     TimerTask& operator=(const TimerTask&) = delete;
153 
154    private:
155     // v8::internal::CancelableTask overrides.
156     void RunInternal() override;
157     MemoryReducer* memory_reducer_;
158   };
159 
160   void NotifyTimer(const Event& event);
161 
162   static bool WatchdogGC(const State& state, const Event& event);
163 
164   Heap* heap_;
165   std::shared_ptr<v8::TaskRunner> taskrunner_;
166   State state_;
167   unsigned int js_calls_counter_;
168   double js_calls_sample_time_ms_;
169 
170   // Used in cctest.
171   friend class heap::HeapTester;
172 };
173 
174 }  // namespace internal
175 }  // namespace v8
176 
177 #endif  // V8_HEAP_MEMORY_REDUCER_H_
178