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_observer_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"
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 MemoryPressureObserverChromeOS::MemoryPressureThresholds thresholds
) {
47 return thresholds
== MemoryPressureObserverChromeOS::
48 THRESHOLD_AGGRESSIVE_CACHE_DISCARD
||
49 thresholds
== MemoryPressureObserverChromeOS::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 MemoryPressureObserverChromeOS::MemoryPressureThresholds thresholds
) {
58 return thresholds
== MemoryPressureObserverChromeOS::
59 THRESHOLD_AGGRESSIVE_TAB_DISCARD
||
60 thresholds
== MemoryPressureObserverChromeOS::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
;
79 MemoryPressureObserverChromeOS::MemoryPressureObserverChromeOS(
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) {
92 MemoryPressureObserverChromeOS::~MemoryPressureObserverChromeOS() {
96 void MemoryPressureObserverChromeOS::ScheduleEarlyCheck() {
97 MessageLoop::current()->PostTask(
99 Bind(&MemoryPressureObserverChromeOS::CheckMemoryPressure
,
100 weak_ptr_factory_
.GetWeakPtr()));
103 void MemoryPressureObserverChromeOS::StartObserving() {
104 timer_
.Start(FROM_HERE
,
105 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs
),
106 Bind(&MemoryPressureObserverChromeOS::
107 CheckMemoryPressureAndRecordStatistics
,
108 weak_ptr_factory_
.GetWeakPtr()));
111 void MemoryPressureObserverChromeOS::StopObserving() {
112 // If StartObserving failed, StopObserving will still get called.
116 void MemoryPressureObserverChromeOS::CheckMemoryPressureAndRecordStatistics() {
117 CheckMemoryPressure();
119 // Record UMA histogram statistics for the current memory pressure level.
120 MemoryPressureLevelUMA
memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE
);
121 switch (current_memory_pressure_level_
) {
122 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
:
123 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_NONE
;
125 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
:
126 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_MODERATE
;
128 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
:
129 memory_pressure_level_uma
= MEMORY_PRESSURE_LEVEL_CRITICAL
;
133 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel",
134 memory_pressure_level_uma
,
135 NUM_MEMORY_PRESSURE_LEVELS
);
138 void MemoryPressureObserverChromeOS::CheckMemoryPressure() {
139 MemoryPressureListener::MemoryPressureLevel old_pressure
=
140 current_memory_pressure_level_
;
141 current_memory_pressure_level_
=
142 GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent(),
143 moderate_pressure_threshold_percent_
,
144 critical_pressure_threshold_percent_
);
145 // In case there is no memory pressure we do not notify.
146 if (current_memory_pressure_level_
==
147 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE
) {
150 if (old_pressure
== current_memory_pressure_level_
) {
151 // If the memory pressure is still at the same level, we notify again for a
152 // critical level. In case of a moderate level repeat however, we only send
153 // a notification after a certain time has passed.
154 if (current_memory_pressure_level_
==
155 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
&&
156 ++moderate_pressure_repeat_count_
<
157 kModerateMemoryPressureCooldown
) {
160 } else if (current_memory_pressure_level_
==
161 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
&&
163 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
) {
164 // When we reducing the pressure level from critical to moderate, we
165 // restart the timeout and do not send another notification.
166 moderate_pressure_repeat_count_
= 0;
169 moderate_pressure_repeat_count_
= 0;
170 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_
);
173 // Gets the used ChromeOS memory in percent.
174 int MemoryPressureObserverChromeOS::GetUsedMemoryInPercent() {
175 base::SystemMemoryInfoKB info
;
176 if (!base::GetSystemMemoryInfo(&info
)) {
177 VLOG(1) << "Cannot determine the free memory of the system.";
180 // TODO(skuhne): Instead of adding the kernel memory pressure calculation
181 // logic here, we should have a kernel mechanism similar to the low memory
182 // notifier in ChromeOS which offers multiple pressure states.
183 // To track this, we have crbug.com/381196.
185 // The available memory consists of "real" and virtual (z)ram memory.
186 // Since swappable memory uses a non pre-deterministic compression and
187 // the compression creates its own "dynamic" in the system, it gets
188 // de-emphasized by the |kSwapWeight| factor.
189 const int kSwapWeight
= 4;
191 // The total memory we have is the "real memory" plus the virtual (z)ram.
192 int total_memory
= info
.total
+ info
.swap_total
/ kSwapWeight
;
194 // The kernel internally uses 50MB.
195 const int kMinFileMemory
= 50 * 1024;
197 // Most file memory can be easily reclaimed.
198 int file_memory
= info
.active_file
+ info
.inactive_file
;
199 // unless it is dirty or it's a minimal portion which is required.
200 file_memory
-= info
.dirty
+ kMinFileMemory
;
202 // Available memory is the sum of free, swap and easy reclaimable memory.
203 int available_memory
=
204 info
.free
+ info
.swap_free
/ kSwapWeight
+ file_memory
;
206 DCHECK(available_memory
< total_memory
);
207 int percentage
= ((total_memory
- available_memory
) * 100) / total_memory
;