v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
memory-reducer.h
Go to the documentation of this file.
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
9#include "src/base/macros.h"
10#include "src/common/globals.h"
12
13namespace v8 {
14namespace internal {
15
16namespace heap {
17class HeapTester;
18} // namespace heap
19
20class 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.
88 public:
89 enum Id { kUninit, kDone, kWait, kRun };
90
91 class State {
92 public:
93 static State CreateUninitialized() { return {kUninit, 0, 0, 0, 0}; }
94
95 static State CreateDone(double last_gc_time_ms, size_t committed_memory) {
96 return {kDone, 0, 0, last_gc_time_ms, committed_memory};
97 }
98
99 static State CreateWait(int started_gcs, double next_gc_time_ms,
100 double last_gc_time_ms) {
101 return {kWait, started_gcs, next_gc_time_ms, last_gc_time_ms, 0};
102 }
103
104 static State CreateRun(int started_gcs) {
105 return {kRun, started_gcs, 0, 0, 0};
106 }
107
108 Id id() const { return id_; }
109
110 int started_gcs() const {
111 DCHECK(id() == kWait || id() == kRun);
112 return started_gcs_;
113 }
114
115 double next_gc_start_ms() const {
116 DCHECK_EQ(id(), kWait);
117 return next_gc_start_ms_;
118 }
119
120 double last_gc_time_ms() const {
121 DCHECK(id() == kWait || id() == kDone || id() == kUninit);
122 return last_gc_time_ms_;
123 }
124
126 DCHECK(id() == kUninit || id() == kDone);
127 return committed_memory_at_last_run_;
128 }
129
130 private:
131 State(Id action, int started_gcs, double next_gc_start_ms,
132 double last_gc_time_ms, size_t committed_memory_at_last_run)
133 : id_(action),
134 started_gcs_(started_gcs),
135 next_gc_start_ms_(next_gc_start_ms),
136 last_gc_time_ms_(last_gc_time_ms),
137 committed_memory_at_last_run_(committed_memory_at_last_run) {}
138
144 };
145
146 enum EventType { kTimer, kMarkCompact, kPossibleGarbage };
147
157
158 explicit MemoryReducer(Heap* heap);
159 MemoryReducer(const MemoryReducer&) = delete;
161 // Callbacks.
162 void NotifyMarkCompact(size_t committed_memory_before);
163 void NotifyPossibleGarbage();
164 // The step function that computes the next state from the current state and
165 // the incoming event.
166 static State Step(const State& state, const Event& event);
167 // Posts a timer task that will call NotifyTimer after the given delay.
168 void ScheduleTimer(double delay_ms);
169 void TearDown();
170 static const int kLongDelayMs;
171 static const int kShortDelayMs;
172 static const int kWatchdogDelayMs;
173 // The committed memory has to increase by at least this factor since the
174 // last run in order to trigger a new run after mark-compact.
175 static const double kCommittedMemoryFactor;
176 // The committed memory has to increase by at least this amount since the
177 // last run in order to trigger a new run after mark-compact.
178 static const size_t kCommittedMemoryDelta;
179
180 Heap* heap() { return heap_; }
181
182 bool ShouldGrowHeapSlowly() { return state_.id() == kDone; }
183
184 static int MaxNumberOfGCs();
185
186 static bool IsFrozen(const Heap* heap);
187
188 private:
190 public:
191 explicit TimerTask(MemoryReducer* memory_reducer);
192 TimerTask(const TimerTask&) = delete;
193 TimerTask& operator=(const TimerTask&) = delete;
194
195 private:
196 // v8::internal::CancelableTask overrides.
197 void RunInternal() override;
199 };
200
201 void NotifyTimer(const Event& event);
202
203 static bool WatchdogGC(const State& state, const Event& event);
204
206 std::shared_ptr<v8::TaskRunner> taskrunner_;
208 unsigned int js_calls_counter_;
210 int start_delay_ms_ = false;
211
212 // Used in cctest.
213 friend class heap::HeapTester;
214};
215
216} // namespace internal
217} // namespace v8
218
219#endif // V8_HEAP_MEMORY_REDUCER_H_
static State CreateWait(int started_gcs, double next_gc_time_ms, double last_gc_time_ms)
State(Id action, int started_gcs, double next_gc_start_ms, double last_gc_time_ms, size_t committed_memory_at_last_run)
static State CreateDone(double last_gc_time_ms, size_t committed_memory)
static State CreateRun(int started_gcs)
TimerTask & operator=(const TimerTask &)=delete
TimerTask(const TimerTask &)=delete
std::shared_ptr< v8::TaskRunner > taskrunner_
static const double kCommittedMemoryFactor
static const size_t kCommittedMemoryDelta
MemoryReducer(const MemoryReducer &)=delete
static const int kWatchdogDelayMs
MemoryReducer & operator=(const MemoryReducer &)=delete
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
Heap * heap_