Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: base/win/memory_pressure_monitor.cc

Issue 1122863005: Create base::win::MemoryPressureMonitor class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed grt's comments. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 // The following constants have been lifted from similar values in the ChromeOS
20 // memory pressure monitor. The values were determined experimentally to ensure
21 // sufficient responsiveness of the memory pressure subsystem, and minimal
22 // overhead.
23
24 // The time between memory pressure checks. While under critical pressure, this
25 // is also the timer to repeat cleanup attempts.
26 const int kMemoryPressureIntervalMs = 1000;
brucedawson 2015/05/06 00:58:27 All of the functions being called should, I believ
chrisha 2015/05/06 02:40:24 Yeah, I profiled GlobalMemoryStatusEx on a handful
27
28 // The time which should pass between two successive moderate memory pressure
29 // calls.
30 const int kModerateMemoryPressureCooldownMs = 10000;
31
32 // Number of event polls before the next moderate pressure event can be sent.
33 const int kModerateMemoryPressureCooldown =
34 kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
35
36 // TODO(chrisha): Explore the following constants further with an experiment.
37
38 // Moderate and critical memory load percentages. These percentages are applied
39 // to both system memory and virtual memory.
40 const int kModerateThresholdPercent = 60;
41 const int kCriticalThresholdPercent = 95;
42
43 // Absolute memory requirements. These are modeled after typical renderer sizes
44 // on Windows sytems (rounded to the neared 10 MB).
Nico 2015/05/05 21:25:11 maybe mention which uma stats one has to look at t
chrisha 2015/05/06 02:40:24 Done.
45 const int kModerateThresholdMb = 300; // 95th percentile renderer size.
46 const int kCriticalThresholdMb = 60; // 50th percentile renderer size.
47
48 // Enumeration of UMA memory pressure levels. This needs to be kept in sync with
49 // histograms.xml and the memory pressure levels defined in
50 // MemoryPressureListener.
51 enum MemoryPressureLevelUMA {
52 UMA_MEMORY_PRESSURE_LEVEL_NONE = 0,
53 UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1,
54 UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
55 // This must be the last value in the enum.
56 UMA_MEMORY_PRESSURE_LEVEL_COUNT,
57 };
58
59 // Converts a memory pressure level to an UMA enumeration value.
60 MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue(
61 MemoryPressureListener::MemoryPressureLevel level) {
62 switch (level) {
63 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
64 return UMA_MEMORY_PRESSURE_LEVEL_NONE;
65 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
66 return UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
67 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
68 return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
69 }
70 NOTREACHED();
71 return UMA_MEMORY_PRESSURE_LEVEL_NONE;
72 }
73
74 } // namespace
75
76 MemoryPressureMonitor::MemoryPressureMonitor()
77 : current_memory_pressure_level_(
78 MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
79 moderate_pressure_repeat_count_(0),
80 weak_ptr_factory_(this) {
81 StartObserving();
82 }
83
84 MemoryPressureMonitor::~MemoryPressureMonitor() {
85 StopObserving();
86 }
87
88 void MemoryPressureMonitor::CheckMemoryPressureSoon() {
89 ThreadTaskRunnerHandle::Get()->PostTask(
90 FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure,
91 weak_ptr_factory_.GetWeakPtr()));
92 }
93
94 MemoryPressureListener::MemoryPressureLevel
95 MemoryPressureMonitor::GetCurrentPressureLevel() const {
96 return current_memory_pressure_level_;
97 }
98
99 void MemoryPressureMonitor::StartObserving() {
100 timer_.Start(FROM_HERE,
101 TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs),
102 Bind(&MemoryPressureMonitor::
103 CheckMemoryPressureAndRecordStatistics,
104 weak_ptr_factory_.GetWeakPtr()));
105 }
106
107 void MemoryPressureMonitor::StopObserving() {
108 // If StartObserving failed, StopObserving will still get called.
109 timer_.Stop();
110 weak_ptr_factory_.InvalidateWeakPtrs();
111 }
112
113 void MemoryPressureMonitor::CheckMemoryPressure() {
114 // |notify| will be set to true if MemoryPressureListeners need to be
115 // notified of a memory pressure level state change.
116 bool notify = false;
117
118 base::AutoLock scoped_lock(lock_);
119 MemoryPressureLevel old_pressure = current_memory_pressure_level_;
120 current_memory_pressure_level_ = CalculateCurrentPressureLevel();
121
122 switch (current_memory_pressure_level_) {
123 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
124 break;
125
126 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: {
127 if (old_pressure != current_memory_pressure_level_) {
128 // This is a new transition to moderate pressure so notify.
129 moderate_pressure_repeat_count_ = 0;
130 notify = true;
131 } else {
132 // Already in moderate pressure, only notify if sustained over the
133 // cooldown period.
134 if (++moderate_pressure_repeat_count_ ==
135 kModerateMemoryPressureCooldown) {
136 moderate_pressure_repeat_count_ = 0;
137 notify = true;
138 }
139 }
140 } break;
141
142 case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: {
143 // Always notify of critical pressure levels.
144 notify = true;
145 } break;
146 }
147
148 if (!notify)
149 return;
150
151 // Emit a notification of the current memory pressure level. This can only
152 // happen for moderate and critical pressure levels.
153 DCHECK_NE(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
154 current_memory_pressure_level_);
155 MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_);
156 }
157
158 void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
159 CheckMemoryPressure();
160
161 UMA_HISTOGRAM_ENUMERATION(
162 "Memory.PressureLevel",
163 MemoryPressureLevelToUmaEnumValue(current_memory_pressure_level_),
164 UMA_MEMORY_PRESSURE_LEVEL_COUNT);
165 }
166
167 MemoryPressureListener::MemoryPressureLevel
168 MemoryPressureMonitor::CalculateCurrentPressureLevel() {
169 _MEMORYSTATUSEX mem_status = {};
170 if (!GetSystemMemoryStatus(&mem_status))
171 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
172
173 // How much of the system's physical memory is actively being used by
174 // pages that are in the working sets of running programs.
175 int phys_load = mem_status.dwMemoryLoad;
176
177 // How much system memory is actively available for use right now, in MBs.
178 int phys_free = static_cast<int>(mem_status.ullAvailPhys / 1024 / 1024);
179
180 // The virtual load of this process. This is the percentage of virtual memory
181 // that is reserved or committed. This is calculated via a double to avoid
182 // overflowing on 64-bit Windows.
183 int virt_load = 100 - static_cast<int>(
184 100.0 * static_cast<double>(mem_status.ullAvailVirtual) /
185 static_cast<double>(mem_status.ullTotalVirtual));
186
187 // The amount of virtual memory that is actively available for use right
188 // now, in MBs.
brucedawson 2015/05/06 00:58:27 This comment should clarify that ullAvailVirtual/v
chrisha 2015/05/06 02:40:24 Done.
189 int virt_free = static_cast<int>(mem_status.ullAvailVirtual / 1024 / 1024);
Nico 2015/05/05 21:25:11 Did you check that this isn't some huge number in
brucedawson 2015/05/06 00:58:27 I agree with Nico. In 64-bit Windows ullAvailVirtu
chrisha 2015/05/06 02:40:24 I agree that there's little value in calculating t
190
191 // Determine if the system is under critical memory pressure.
192 if (phys_load > kCriticalThresholdPercent ||
193 virt_load > kCriticalThresholdPercent ||
194 phys_free < kCriticalThresholdMb ||
195 virt_free < kCriticalThresholdMb) {
196 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
197 }
198
199 // Determine if the system is under moderate memory pressure.
200 if (phys_load > kModerateThresholdPercent ||
201 virt_load > kModerateThresholdPercent ||
202 phys_free < kModerateThresholdMb ||
brucedawson 2015/05/06 00:58:28 I'm not sure this test really works. My understand
chrisha 2015/05/06 02:40:24 The absolute thresholds are only meant for low-end
203 virt_free < kModerateThresholdMb) {
204 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
205 }
206
207 // No memory pressure was detected.
208 return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
209 }
210
211 bool MemoryPressureMonitor::GetSystemMemoryStatus(
212 _MEMORYSTATUSEX* mem_status) {
213 DCHECK(mem_status != nullptr);
214 mem_status->dwLength = sizeof(_MEMORYSTATUSEX);
215 if (!::GlobalMemoryStatusEx(mem_status))
216 return false;
217 return true;
218 }
219
220 } // namespace win
221 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698