Migrate ChromeOS to base::MemoryPressureMonitor
[chromium-blink-merge.git] / base / chromeos / memory_pressure_monitor_chromeos.cc
blob4523ff5d75b2a06a703c11bed28a6bd6467bb293
1 // Copyright 2014 The Chromium 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.
5 #include "base/chromeos/memory_pressure_monitor_chromeos.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/process/process_metrics.h"
10 #include "base/time/time.h"
12 namespace base {
14 namespace {
16 // The time between memory pressure checks. While under critical pressure, this
17 // is also the timer to repeat cleanup attempts.
18 const int kMemoryPressureIntervalMs = 1000;
20 // The time which should pass between two moderate memory pressure calls.
21 const int kModerateMemoryPressureCooldownMs = 10000;
23 // Number of event polls before the next moderate pressure event can be sent.
24 const int kModerateMemoryPressureCooldown =
25 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
27 // Threshold constants to emit pressure events.
28 const int kNormalMemoryPressureModerateThresholdPercent = 60;
29 const int kNormalMemoryPressureCriticalThresholdPercent = 90;
30 const int kAggressiveMemoryPressureModerateThresholdPercent = 35;
31 const int kAggressiveMemoryPressureCriticalThresholdPercent = 70;
33 // The possible state for memory pressure level. The values should be in line
34 // with values in MemoryPressureListener::MemoryPressureLevel and should be
35 // updated if more memory pressure levels are introduced.
36 enum MemoryPressureLevelUMA {
37 MEMORY_PRESSURE_LEVEL_NONE = 0,
38 MEMORY_PRESSURE_LEVEL_MODERATE,
39 MEMORY_PRESSURE_LEVEL_CRITICAL,
40 NUM_MEMORY_PRESSURE_LEVELS
43 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
44 // the moderate pressure event.
45 int GetModerateMemoryThresholdInPercent(
46 MemoryPressureMonitorChromeOS::MemoryPressureThresholds thresholds) {
47 return thresholds == MemoryPressureMonitorChromeOS::
48 THRESHOLD_AGGRESSIVE_CACHE_DISCARD ||
49 thresholds == MemoryPressureMonitorChromeOS::THRESHOLD_AGGRESSIVE
50 ? kAggressiveMemoryPressureModerateThresholdPercent
51 : kNormalMemoryPressureModerateThresholdPercent;
54 // Converts a |MemoryPressureThreshold| value into a used memory percentage for
55 // the critical pressure event.
56 int GetCriticalMemoryThresholdInPercent(
57 MemoryPressureMonitorChromeOS::MemoryPressureThresholds thresholds) {
58 return thresholds == MemoryPressureMonitorChromeOS::
59 THRESHOLD_AGGRESSIVE_TAB_DISCARD ||
60 thresholds == MemoryPressureMonitorChromeOS::THRESHOLD_AGGRESSIVE
61 ? kAggressiveMemoryPressureCriticalThresholdPercent
62 : kNormalMemoryPressureCriticalThresholdPercent;
65 // Converts free percent of memory into a memory pressure value.
66 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
67 int actual_fill_level,
68 int moderate_threshold,
69 int critical_threshold) {
70 if (actual_fill_level < moderate_threshold)
71 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
72 return actual_fill_level < critical_threshold
73 ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
74 : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
77 } // namespace
79 MemoryPressureMonitorChromeOS::MemoryPressureMonitorChromeOS(
80 MemoryPressureThresholds thresholds)
81 : current_memory_pressure_level_(
82 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
83 moderate_pressure_repeat_count_(0),
84 moderate_pressure_threshold_percent_(
85 GetModerateMemoryThresholdInPercent(thresholds)),
86 critical_pressure_threshold_percent_(
87 GetCriticalMemoryThresholdInPercent(thresholds)),
88 weak_ptr_factory_(this) {
89 StartObserving();
92 MemoryPressureMonitorChromeOS::~MemoryPressureMonitorChromeOS() {
93 StopObserving();
96 void MemoryPressureMonitorChromeOS::ScheduleEarlyCheck() {
97 MessageLoop::current()->PostTask(
98 FROM_HERE,
99 Bind(&MemoryPressureMonitorChromeOS::CheckMemoryPressure,
100 weak_ptr_factory_.GetWeakPtr()));
103 MemoryPressureListener::MemoryPressureLevel
104 MemoryPressureMonitorChromeOS::GetCurrentPressureLevel() const {
105 return current_memory_pressure_level_;
108 void MemoryPressureMonitorChromeOS::StartObserving() {
109 timer_.Start(FROM_HERE,
110 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs),
111 Bind(&MemoryPressureMonitorChromeOS::
112 CheckMemoryPressureAndRecordStatistics,
113 weak_ptr_factory_.GetWeakPtr()));
116 void MemoryPressureMonitorChromeOS::StopObserving() {
117 // If StartObserving failed, StopObserving will still get called.
118 timer_.Stop();
121 void MemoryPressureMonitorChromeOS::CheckMemoryPressureAndRecordStatistics() {
122 CheckMemoryPressure();
124 // Record UMA histogram statistics for the current memory pressure level.
125 MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE);
126 switch (current_memory_pressure_level_) {
127 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
128 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE;
129 break;
130 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
131 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE;
132 break;
133 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
134 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL;
135 break;
138 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel",
139 memory_pressure_level_uma,
140 NUM_MEMORY_PRESSURE_LEVELS);
143 void MemoryPressureMonitorChromeOS::CheckMemoryPressure() {
144 MemoryPressureListener::MemoryPressureLevel old_pressure =
145 current_memory_pressure_level_;
146 current_memory_pressure_level_ =
147 GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent(),
148 moderate_pressure_threshold_percent_,
149 critical_pressure_threshold_percent_);
150 // In case there is no memory pressure we do not notify.
151 if (current_memory_pressure_level_ ==
152 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
153 return;
155 if (old_pressure == current_memory_pressure_level_) {
156 // If the memory pressure is still at the same level, we notify again for a
157 // critical level. In case of a moderate level repeat however, we only send
158 // a notification after a certain time has passed.
159 if (current_memory_pressure_level_ ==
160 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
161 ++moderate_pressure_repeat_count_ <
162 kModerateMemoryPressureCooldown) {
163 return;
165 } else if (current_memory_pressure_level_ ==
166 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
167 old_pressure ==
168 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
169 // When we reducing the pressure level from critical to moderate, we
170 // restart the timeout and do not send another notification.
171 moderate_pressure_repeat_count_ = 0;
172 return;
174 moderate_pressure_repeat_count_ = 0;
175 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_);
178 // Gets the used ChromeOS memory in percent.
179 int MemoryPressureMonitorChromeOS::GetUsedMemoryInPercent() {
180 base::SystemMemoryInfoKB info;
181 if (!base::GetSystemMemoryInfo(&info)) {
182 VLOG(1) << "Cannot determine the free memory of the system.";
183 return 0;
185 // TODO(skuhne): Instead of adding the kernel memory pressure calculation
186 // logic here, we should have a kernel mechanism similar to the low memory
187 // notifier in ChromeOS which offers multiple pressure states.
188 // To track this, we have crbug.com/381196.
190 // The available memory consists of "real" and virtual (z)ram memory.
191 // Since swappable memory uses a non pre-deterministic compression and
192 // the compression creates its own "dynamic" in the system, it gets
193 // de-emphasized by the |kSwapWeight| factor.
194 const int kSwapWeight = 4;
196 // The total memory we have is the "real memory" plus the virtual (z)ram.
197 int total_memory = info.total + info.swap_total / kSwapWeight;
199 // The kernel internally uses 50MB.
200 const int kMinFileMemory = 50 * 1024;
202 // Most file memory can be easily reclaimed.
203 int file_memory = info.active_file + info.inactive_file;
204 // unless it is dirty or it's a minimal portion which is required.
205 file_memory -= info.dirty + kMinFileMemory;
207 // Available memory is the sum of free, swap and easy reclaimable memory.
208 int available_memory =
209 info.free + info.swap_free / kSwapWeight + file_memory;
211 DCHECK(available_memory < total_memory);
212 int percentage = ((total_memory - available_memory) * 100) / total_memory;
213 return percentage;
216 } // namespace base