OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "base/chromeos/memory_pressure_observer_chromeos.h" | |
6 | |
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" | |
11 | |
12 namespace base { | |
13 | |
14 namespace { | |
15 | |
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; | |
19 | |
20 // The time which should pass between two moderate memory pressure calls. | |
21 const int kModerateMemoryPressureCooldownMs = 10000; | |
22 | |
23 // Number of event polls before the next moderate pressure event can be sent. | |
24 const int kModerateMemoryPressureCooldown = | |
25 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; | |
26 | |
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; | |
32 | |
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 | |
41 }; | |
42 | |
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; | |
52 } | |
53 | |
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; | |
63 } | |
64 | |
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; | |
75 } | |
76 | |
77 } // namespace | |
78 | |
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) { | |
89 StartObserving(); | |
90 } | |
91 | |
92 MemoryPressureObserverChromeOS::~MemoryPressureObserverChromeOS() { | |
93 StopObserving(); | |
94 } | |
95 | |
96 void MemoryPressureObserverChromeOS::ScheduleEarlyCheck() { | |
97 MessageLoop::current()->PostTask( | |
98 FROM_HERE, | |
99 Bind(&MemoryPressureObserverChromeOS::CheckMemoryPressure, | |
100 weak_ptr_factory_.GetWeakPtr())); | |
101 } | |
102 | |
103 void MemoryPressureObserverChromeOS::StartObserving() { | |
104 timer_.Start(FROM_HERE, | |
105 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), | |
106 Bind(&MemoryPressureObserverChromeOS:: | |
107 CheckMemoryPressureAndRecordStatistics, | |
108 weak_ptr_factory_.GetWeakPtr())); | |
109 } | |
110 | |
111 void MemoryPressureObserverChromeOS::StopObserving() { | |
112 // If StartObserving failed, StopObserving will still get called. | |
113 timer_.Stop(); | |
114 } | |
115 | |
116 void MemoryPressureObserverChromeOS::CheckMemoryPressureAndRecordStatistics() { | |
117 CheckMemoryPressure(); | |
118 | |
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; | |
124 break; | |
125 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
126 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE; | |
127 break; | |
128 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
129 memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL; | |
130 break; | |
131 } | |
132 | |
133 UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", | |
134 memory_pressure_level_uma, | |
135 NUM_MEMORY_PRESSURE_LEVELS); | |
136 } | |
137 | |
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) { | |
148 return; | |
149 } | |
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) { | |
158 return; | |
159 } | |
160 } else if (current_memory_pressure_level_ == | |
161 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && | |
162 old_pressure == | |
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; | |
167 return; | |
168 } | |
169 moderate_pressure_repeat_count_ = 0; | |
170 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); | |
171 } | |
172 | |
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."; | |
178 return 0; | |
179 } | |
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. | |
184 | |
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; | |
190 | |
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; | |
193 | |
194 // The kernel internally uses 50MB. | |
195 const int kMinFileMemory = 50 * 1024; | |
196 | |
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; | |
201 | |
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; | |
205 | |
206 DCHECK(available_memory < total_memory); | |
207 int percentage = ((total_memory - available_memory) * 100) / total_memory; | |
208 return percentage; | |
209 } | |
210 | |
211 } // namespace base | |
OLD | NEW |