memory-reducer.h 6.35 KB
Newer Older
1 2 3 4
// 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.

5 6
#ifndef V8_HEAP_MEMORY_REDUCER_H_
#define V8_HEAP_MEMORY_REDUCER_H_
7 8 9

#include "include/v8-platform.h"
#include "src/base/macros.h"
10 11
#include "src/common/globals.h"
#include "src/tasks/cancelable-task.h"
12 13 14 15

namespace v8 {
namespace internal {

16 17 18 19
namespace heap {
class HeapTester;
}  // namespace heap

20 21 22 23 24 25 26 27 28 29
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:
30 31 32
// - DONE <last_gc_time_ms>
// - WAIT <started_gcs> <next_gc_start_ms> <last_gc_time_ms>
// - RUN <started_gcs> <last_gc_time_ms>
33 34 35 36 37
// 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.
38
// The <last_gc_start_ms> is a double that stores the time of the last full GC.
39 40 41
// 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
42 43
// callback. If the allocation rate does not drop in watchdog_delay_ms since
// the last GC then transition to the RUN state is forced.
44 45 46 47 48
// 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:
49 50
// DONE t -> WAIT 0 (now_ms + long_delay_ms) t' happens:
//     - on context disposal.
51 52 53
//     - at the end of mark-compact GC initiated by the mutator.
// This signals that there is potential garbage to be collected.
//
54
// WAIT n x t -> WAIT n (now_ms + long_delay_ms) t' happens:
55 56
//     - on mark-compact GC initiated by the mutator,
//     - 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 61 62 63 64
//     - 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.
//
65
// WAIT n x t -> DONE t happens:
66 67
//     - in the timer callback if n >= kMaxNumberOfGCs.
//
68
// WAIT n x t -> RUN (n+1) t happens:
69 70
//     - in the timer callback if the mutator allocation rate is low
//       and now_ms >= x and there is no incremental GC in progress.
71 72
//     - in the timer callback if (now_ms - t > watchdog_delay_ms) and
//       and now_ms >= x and there is no incremental GC in progress.
73 74
// The MemoryReducer starts incremental marking on this transition.
//
75
// RUN n t -> DONE now_ms happens:
76 77 78
//     - 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.
79
// RUN n t -> WAIT n (now_ms + short_delay_ms) now_ms happens:
80 81 82 83
//     - at end of the incremental GC initiated by the MemoryReducer if
//       (n == 1 or there is more garbage to be collected) and
//       n < kMaxNumberOfGCs.
//
84 85 86
// 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.
87
class V8_EXPORT_PRIVATE MemoryReducer {
88 89 90 91
 public:
  enum Action { kDone, kWait, kRun };

  struct State {
92
    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 95
        : action(action),
          started_gcs(started_gcs),
96
          next_gc_start_ms(next_gc_start_ms),
97 98
          last_gc_time_ms(last_gc_time_ms),
          committed_memory_at_last_run(committed_memory_at_last_run) {}
99 100 101
    Action action;
    int started_gcs;
    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 109 110

  struct Event {
    EventType type;
    double time_ms;
111
    size_t committed_memory;
112
    bool next_gc_likely_to_collect_more;
113
    bool should_start_incremental_gc;
114 115 116
    bool can_start_incremental_gc;
  };

117
  explicit MemoryReducer(Heap* heap);
118 119
  // Callbacks.
  void NotifyMarkCompact(const Event& event);
120
  void NotifyPossibleGarbage(const Event& event);
121
  void NotifyBackgroundIdleNotification(const Event& event);
122 123 124 125
  // 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.
126
  void ScheduleTimer(double delay_ms);
127
  void TearDown();
128 129
  static const int kLongDelayMs;
  static const int kShortDelayMs;
130
  static const int kWatchdogDelayMs;
131
  static const int kMaxNumberOfGCs;
132 133 134 135 136 137
  // 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;
138 139 140

  Heap* heap() { return heap_; }

141 142 143 144
  bool ShouldGrowHeapSlowly() {
    return state_.action == kDone && state_.started_gcs > 0;
  }

145
 private:
146
  class TimerTask : public v8::internal::CancelableTask {
147
   public:
148
    explicit TimerTask(MemoryReducer* memory_reducer);
149 150

   private:
151 152
    // v8::internal::CancelableTask overrides.
    void RunInternal() override;
153 154 155
    MemoryReducer* memory_reducer_;
    DISALLOW_COPY_AND_ASSIGN(TimerTask);
  };
156 157 158

  void NotifyTimer(const Event& event);

159 160
  static bool WatchdogGC(const State& state, const Event& event);

161
  Heap* heap_;
162
  std::shared_ptr<v8::TaskRunner> taskrunner_;
163
  State state_;
164 165 166 167
  unsigned int js_calls_counter_;
  double js_calls_sample_time_ms_;

  // Used in cctest.
168
  friend class heap::HeapTester;
169 170 171 172 173 174
  DISALLOW_COPY_AND_ASSIGN(MemoryReducer);
};

}  // namespace internal
}  // namespace v8

175
#endif  // V8_HEAP_MEMORY_REDUCER_H_