summaryrefslogtreecommitdiff
path: root/deps/v8/src/heap/memory-reducer.h
blob: 90a955150e415f45bf144e2e64cdaf07f4034f99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// 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/cancelable-task.h"
#include "src/globals.h"

namespace v8 {
namespace internal {

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 HeapTester;
  DISALLOW_COPY_AND_ASSIGN(MemoryReducer);
};

}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_MEMORY_REDUCER_H_