OLD | NEW |
| (Empty) |
1 // Copyright 2015 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/win/memory_pressure_monitor.h" | |
6 | |
7 #include <windows.h> | |
8 | |
9 #include "base/metrics/histogram_macros.h" | |
10 #include "base/single_thread_task_runner.h" | |
11 #include "base/thread_task_runner_handle.h" | |
12 #include "base/time/time.h" | |
13 | |
14 namespace base { | |
15 namespace win { | |
16 | |
17 namespace { | |
18 | |
19 static const DWORDLONG kMBBytes = 1024 * 1024; | |
20 | |
21 // Enumeration of UMA memory pressure levels. This needs to be kept in sync with | |
22 // histograms.xml and the memory pressure levels defined in | |
23 // MemoryPressureListener. | |
24 enum MemoryPressureLevelUMA { | |
25 UMA_MEMORY_PRESSURE_LEVEL_NONE = 0, | |
26 UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1, | |
27 UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2, | |
28 // This must be the last value in the enum. | |
29 UMA_MEMORY_PRESSURE_LEVEL_COUNT, | |
30 }; | |
31 | |
32 // Converts a memory pressure level to an UMA enumeration value. | |
33 MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue( | |
34 MemoryPressureListener::MemoryPressureLevel level) { | |
35 switch (level) { | |
36 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | |
37 return UMA_MEMORY_PRESSURE_LEVEL_NONE; | |
38 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
39 return UMA_MEMORY_PRESSURE_LEVEL_MODERATE; | |
40 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
41 return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL; | |
42 } | |
43 NOTREACHED(); | |
44 return UMA_MEMORY_PRESSURE_LEVEL_NONE; | |
45 } | |
46 | |
47 } // namespace | |
48 | |
49 // The following constants have been lifted from similar values in the ChromeOS | |
50 // memory pressure monitor. The values were determined experimentally to ensure | |
51 // sufficient responsiveness of the memory pressure subsystem, and minimal | |
52 // overhead. | |
53 const int MemoryPressureMonitor::kPollingIntervalMs = 5000; | |
54 const int MemoryPressureMonitor::kModeratePressureCooldownMs = 10000; | |
55 const int MemoryPressureMonitor::kModeratePressureCooldownCycles = | |
56 kModeratePressureCooldownMs / kPollingIntervalMs; | |
57 | |
58 // TODO(chrisha): Explore the following constants further with an experiment. | |
59 | |
60 // A system is considered 'high memory' if it has more than 1.5GB of system | |
61 // memory available for use by the memory manager (not reserved for hardware | |
62 // and drivers). This is a fuzzy version of the ~2GB discussed below. | |
63 const int MemoryPressureMonitor::kLargeMemoryThresholdMb = 1536; | |
64 | |
65 // These are the default thresholds used for systems with < ~2GB of physical | |
66 // memory. Such systems have been observed to always maintain ~100MB of | |
67 // available memory, paging until that is the case. To try to avoid paging a | |
68 // threshold slightly above this is chosen. The moderate threshold is slightly | |
69 // less grounded in reality and chosen as 2.5x critical. | |
70 const int MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb = 500; | |
71 const int MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb = 200; | |
72 | |
73 // These are the default thresholds used for systems with >= ~2GB of physical | |
74 // memory. Such systems have been observed to always maintain ~300MB of | |
75 // available memory, paging until that is the case. | |
76 const int MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb = 1000; | |
77 const int MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb = 400; | |
78 | |
79 MemoryPressureMonitor::MemoryPressureMonitor() | |
80 : moderate_threshold_mb_(0), | |
81 critical_threshold_mb_(0), | |
82 current_memory_pressure_level_( | |
83 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), | |
84 moderate_pressure_repeat_count_(0), | |
85 weak_ptr_factory_(this) { | |
86 InferThresholds(); | |
87 StartObserving(); | |
88 } | |
89 | |
90 MemoryPressureMonitor::MemoryPressureMonitor(int moderate_threshold_mb, | |
91 int critical_threshold_mb) | |
92 : moderate_threshold_mb_(moderate_threshold_mb), | |
93 critical_threshold_mb_(critical_threshold_mb), | |
94 current_memory_pressure_level_( | |
95 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), | |
96 moderate_pressure_repeat_count_(0), | |
97 weak_ptr_factory_(this) { | |
98 DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_); | |
99 DCHECK_LE(0, critical_threshold_mb_); | |
100 StartObserving(); | |
101 } | |
102 | |
103 MemoryPressureMonitor::~MemoryPressureMonitor() { | |
104 StopObserving(); | |
105 } | |
106 | |
107 void MemoryPressureMonitor::CheckMemoryPressureSoon() { | |
108 DCHECK(thread_checker_.CalledOnValidThread()); | |
109 | |
110 ThreadTaskRunnerHandle::Get()->PostTask( | |
111 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, | |
112 weak_ptr_factory_.GetWeakPtr())); | |
113 } | |
114 | |
115 MemoryPressureListener::MemoryPressureLevel | |
116 MemoryPressureMonitor::GetCurrentPressureLevel() const { | |
117 return current_memory_pressure_level_; | |
118 } | |
119 | |
120 void MemoryPressureMonitor::InferThresholds() { | |
121 // Default to a 'high' memory situation, which uses more conservative | |
122 // thresholds. | |
123 bool high_memory = true; | |
124 MEMORYSTATUSEX mem_status = {}; | |
125 if (GetSystemMemoryStatus(&mem_status)) { | |
126 static const DWORDLONG kLargeMemoryThresholdBytes = | |
127 static_cast<DWORDLONG>(kLargeMemoryThresholdMb) * kMBBytes; | |
128 high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes; | |
129 } | |
130 | |
131 if (high_memory) { | |
132 moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb; | |
133 critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb; | |
134 } else { | |
135 moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb; | |
136 critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb; | |
137 } | |
138 } | |
139 | |
140 void MemoryPressureMonitor::StartObserving() { | |
141 DCHECK(thread_checker_.CalledOnValidThread()); | |
142 | |
143 timer_.Start(FROM_HERE, | |
144 TimeDelta::FromMilliseconds(kPollingIntervalMs), | |
145 Bind(&MemoryPressureMonitor:: | |
146 CheckMemoryPressureAndRecordStatistics, | |
147 weak_ptr_factory_.GetWeakPtr())); | |
148 } | |
149 | |
150 void MemoryPressureMonitor::StopObserving() { | |
151 DCHECK(thread_checker_.CalledOnValidThread()); | |
152 | |
153 // If StartObserving failed, StopObserving will still get called. | |
154 timer_.Stop(); | |
155 weak_ptr_factory_.InvalidateWeakPtrs(); | |
156 } | |
157 | |
158 void MemoryPressureMonitor::CheckMemoryPressure() { | |
159 DCHECK(thread_checker_.CalledOnValidThread()); | |
160 | |
161 // Get the previous pressure level and update the current one. | |
162 MemoryPressureLevel old_pressure = current_memory_pressure_level_; | |
163 current_memory_pressure_level_ = CalculateCurrentPressureLevel(); | |
164 | |
165 // |notify| will be set to true if MemoryPressureListeners need to be | |
166 // notified of a memory pressure level state change. | |
167 bool notify = false; | |
168 switch (current_memory_pressure_level_) { | |
169 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: | |
170 break; | |
171 | |
172 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: | |
173 if (old_pressure != current_memory_pressure_level_) { | |
174 // This is a new transition to moderate pressure so notify. | |
175 moderate_pressure_repeat_count_ = 0; | |
176 notify = true; | |
177 } else { | |
178 // Already in moderate pressure, only notify if sustained over the | |
179 // cooldown period. | |
180 if (++moderate_pressure_repeat_count_ == | |
181 kModeratePressureCooldownCycles) { | |
182 moderate_pressure_repeat_count_ = 0; | |
183 notify = true; | |
184 } | |
185 } | |
186 break; | |
187 | |
188 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: | |
189 // Always notify of critical pressure levels. | |
190 notify = true; | |
191 break; | |
192 } | |
193 | |
194 if (!notify) | |
195 return; | |
196 | |
197 // Emit a notification of the current memory pressure level. This can only | |
198 // happen for moderate and critical pressure levels. | |
199 DCHECK_NE(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, | |
200 current_memory_pressure_level_); | |
201 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); | |
202 } | |
203 | |
204 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { | |
205 DCHECK(thread_checker_.CalledOnValidThread()); | |
206 | |
207 CheckMemoryPressure(); | |
208 | |
209 UMA_HISTOGRAM_ENUMERATION( | |
210 "Memory.PressureLevel", | |
211 MemoryPressureLevelToUmaEnumValue(current_memory_pressure_level_), | |
212 UMA_MEMORY_PRESSURE_LEVEL_COUNT); | |
213 } | |
214 | |
215 MemoryPressureListener::MemoryPressureLevel | |
216 MemoryPressureMonitor::CalculateCurrentPressureLevel() { | |
217 MEMORYSTATUSEX mem_status = {}; | |
218 if (!GetSystemMemoryStatus(&mem_status)) | |
219 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | |
220 | |
221 // How much system memory is actively available for use right now, in MBs. | |
222 int phys_free = static_cast<int>(mem_status.ullAvailPhys / kMBBytes); | |
223 | |
224 // TODO(chrisha): This should eventually care about address space pressure, | |
225 // but the browser process (where this is running) effectively never runs out | |
226 // of address space. Renderers occasionally do, but it does them no good to | |
227 // have the browser process monitor address space pressure. Long term, | |
228 // renderers should run their own address space pressure monitors and act | |
229 // accordingly, with the browser making cross-process decisions based on | |
230 // system memory pressure. | |
231 | |
232 // Determine if the physical memory is under critical memory pressure. | |
233 if (phys_free <= critical_threshold_mb_) | |
234 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; | |
235 | |
236 // Determine if the physical memory is under moderate memory pressure. | |
237 if (phys_free <= moderate_threshold_mb_) | |
238 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; | |
239 | |
240 // No memory pressure was detected. | |
241 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; | |
242 } | |
243 | |
244 bool MemoryPressureMonitor::GetSystemMemoryStatus( | |
245 MEMORYSTATUSEX* mem_status) { | |
246 DCHECK(mem_status != nullptr); | |
247 mem_status->dwLength = sizeof(*mem_status); | |
248 if (!::GlobalMemoryStatusEx(mem_status)) | |
249 return false; | |
250 return true; | |
251 } | |
252 | |
253 } // namespace win | |
254 } // namespace base | |
OLD | NEW |