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

Side by Side Diff: content/browser/memory/memory_coordinator_impl.cc

Issue 2568393002: Separate state updating code from MemoryCoordinatorImpl (Closed)
Patch Set: rebase Created 4 years 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/memory/memory_coordinator_impl.h" 5 #include "content/browser/memory/memory_coordinator_impl.h"
6 6
7 #include "base/metrics/histogram_macros.h" 7 #include "base/metrics/histogram_macros.h"
8 #include "base/process/process_metrics.h" 8 #include "base/process/process_metrics.h"
9 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "base/threading/thread_task_runner_handle.h" 10 #include "base/threading/thread_task_runner_handle.h"
11 #include "base/trace_event/trace_event.h" 11 #include "base/trace_event/trace_event.h"
12 #include "components/variations/variations_associated_data.h"
13 #include "content/browser/memory/memory_monitor.h" 12 #include "content/browser/memory/memory_monitor.h"
13 #include "content/browser/memory/memory_state_updater.h"
14 #include "content/public/browser/notification_service.h" 14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h" 15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_process_host.h" 16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/render_widget_host.h" 17 #include "content/public/browser/render_widget_host.h"
18 #include "content/public/common/content_features.h" 18 #include "content/public/common/content_features.h"
19 19
20 namespace content { 20 namespace content {
21 21
22 namespace { 22 namespace {
23 23
24 // A expected renderer size. These values come from the median of appropriate
25 // UMA stats.
26 #if defined(OS_ANDROID) || defined(OS_IOS)
27 const int kDefaultExpectedRendererSizeMB = 40;
28 #elif defined(OS_WIN)
29 const int kDefaultExpectedRendererSizeMB = 70;
30 #else // Mac, Linux, and ChromeOS
31 const int kDefaultExpectedRendererSizeMB = 120;
32 #endif
33
34 // Default values for parameters to determine the global state.
35 const int kDefaultNewRenderersUntilThrottled = 4;
36 const int kDefaultNewRenderersUntilSuspended = 2;
37 const int kDefaultNewRenderersBackToNormal = 5;
38 const int kDefaultNewRenderersBackToThrottled = 3;
39 const int kDefaultMinimumTransitionPeriodSeconds = 30;
40 const int kDefaultMonitoringIntervalSeconds = 5;
41
42 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) { 24 mojom::MemoryState ToMojomMemoryState(base::MemoryState state) {
43 switch (state) { 25 switch (state) {
44 case base::MemoryState::UNKNOWN: 26 case base::MemoryState::UNKNOWN:
45 return mojom::MemoryState::UNKNOWN; 27 return mojom::MemoryState::UNKNOWN;
46 case base::MemoryState::NORMAL: 28 case base::MemoryState::NORMAL:
47 return mojom::MemoryState::NORMAL; 29 return mojom::MemoryState::NORMAL;
48 case base::MemoryState::THROTTLED: 30 case base::MemoryState::THROTTLED:
49 return mojom::MemoryState::THROTTLED; 31 return mojom::MemoryState::THROTTLED;
50 case base::MemoryState::SUSPENDED: 32 case base::MemoryState::SUSPENDED:
51 return mojom::MemoryState::SUSPENDED; 33 return mojom::MemoryState::SUSPENDED;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 case base::MemoryState::SUSPENDED: 86 case base::MemoryState::SUSPENDED:
105 NOTREACHED(); 87 NOTREACHED();
106 break; 88 break;
107 } 89 }
108 } else { 90 } else {
109 NOTREACHED(); 91 NOTREACHED();
110 } 92 }
111 #undef RECORD_METRICS 93 #undef RECORD_METRICS
112 } 94 }
113 95
114 void SetIntVariationParameter(const std::map<std::string, std::string> params,
115 const char* name,
116 int* target) {
117 const auto& iter = params.find(name);
118 if (iter == params.end())
119 return;
120 int value;
121 if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
122 DCHECK(value > 0);
123 *target = value;
124 }
125 }
126
127 void SetSecondsVariationParameter(
128 const std::map<std::string, std::string> params,
129 const char* name,
130 base::TimeDelta* target) {
131 const auto& iter = params.find(name);
132 if (iter == params.end())
133 return;
134 int value;
135 if (!iter->second.empty() && base::StringToInt(iter->second, &value)) {
136 DCHECK(value > 0);
137 *target = base::TimeDelta::FromSeconds(value);
138 }
139 }
140
141 } // namespace 96 } // namespace
142 97
143 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl 98 // SingletonTraits for MemoryCoordinator. Returns MemoryCoordinatorImpl
144 // as an actual instance. 99 // as an actual instance.
145 struct MemoryCoordinatorSingletonTraits 100 struct MemoryCoordinatorSingletonTraits
146 : public base::LeakySingletonTraits<MemoryCoordinator> { 101 : public base::LeakySingletonTraits<MemoryCoordinator> {
147 static MemoryCoordinator* New() { 102 static MemoryCoordinator* New() {
148 return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(), 103 return new MemoryCoordinatorImpl(base::ThreadTaskRunnerHandle::Get(),
149 CreateMemoryMonitor()); 104 CreateMemoryMonitor());
150 } 105 }
151 }; 106 };
152 107
153 // static 108 // static
154 MemoryCoordinator* MemoryCoordinator::GetInstance() { 109 MemoryCoordinator* MemoryCoordinator::GetInstance() {
155 if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator)) 110 if (!base::FeatureList::IsEnabled(features::kMemoryCoordinator))
156 return nullptr; 111 return nullptr;
157 return base::Singleton<MemoryCoordinator, 112 return base::Singleton<MemoryCoordinator,
158 MemoryCoordinatorSingletonTraits>::get(); 113 MemoryCoordinatorSingletonTraits>::get();
159 } 114 }
160 115
161 MemoryCoordinatorImpl::MemoryCoordinatorImpl( 116 MemoryCoordinatorImpl::MemoryCoordinatorImpl(
162 scoped_refptr<base::SingleThreadTaskRunner> task_runner, 117 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
163 std::unique_ptr<MemoryMonitor> memory_monitor) 118 std::unique_ptr<MemoryMonitor> memory_monitor)
164 : task_runner_(task_runner), 119 : memory_monitor_(std::move(memory_monitor)),
165 memory_monitor_(std::move(memory_monitor)), 120 state_updater_(base::MakeUnique<MemoryStateUpdater>(this, task_runner)) {
166 weak_ptr_factory_(this) {
167 DCHECK(memory_monitor_.get()); 121 DCHECK(memory_monitor_.get());
168 InitializeParameters();
169 } 122 }
170 123
171 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {} 124 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {}
172 125
173 void MemoryCoordinatorImpl::Start() { 126 void MemoryCoordinatorImpl::Start() {
174 DCHECK(CalledOnValidThread()); 127 DCHECK(CalledOnValidThread());
175 DCHECK(last_state_change_.is_null()); 128 DCHECK(last_state_change_.is_null());
176 DCHECK(ValidateParameters());
177 129
178 notification_registrar_.Add( 130 notification_registrar_.Add(
179 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, 131 this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
180 NotificationService::AllBrowserContextsAndSources()); 132 NotificationService::AllBrowserContextsAndSources());
181 last_state_change_ = base::TimeTicks::Now(); 133 last_state_change_ = base::TimeTicks::Now();
182 ScheduleUpdateState(base::TimeDelta()); 134 state_updater_->ScheduleUpdateState(base::TimeDelta());
183 } 135 }
184 136
185 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) { 137 void MemoryCoordinatorImpl::OnChildAdded(int render_process_id) {
186 // Populate the global state as an initial state of a newly created process. 138 // Populate the global state as an initial state of a newly created process.
187 auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); 139 auto new_state = ToMojomMemoryState(GetGlobalMemoryState());
188 SetChildMemoryState(render_process_id, new_state); 140 SetChildMemoryState(render_process_id, new_state);
189 } 141 }
190 142
191 base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const { 143 base::MemoryState MemoryCoordinatorImpl::GetGlobalMemoryState() const {
192 return current_state_; 144 return current_state_;
(...skipping 11 matching lines...) Expand all
204 base::MemoryState memory_state) { 156 base::MemoryState memory_state) {
205 // This changes the current state temporariy for testing. The state will be 157 // This changes the current state temporariy for testing. The state will be
206 // updated 1 minute later. 158 // updated 1 minute later.
207 ForceSetGlobalState(memory_state, base::TimeDelta::FromMinutes(1)); 159 ForceSetGlobalState(memory_state, base::TimeDelta::FromMinutes(1));
208 } 160 }
209 161
210 void MemoryCoordinatorImpl::ForceSetGlobalState(base::MemoryState new_state, 162 void MemoryCoordinatorImpl::ForceSetGlobalState(base::MemoryState new_state,
211 base::TimeDelta duration) { 163 base::TimeDelta duration) {
212 DCHECK(new_state != MemoryState::UNKNOWN); 164 DCHECK(new_state != MemoryState::UNKNOWN);
213 ChangeStateIfNeeded(current_state_, new_state); 165 ChangeStateIfNeeded(current_state_, new_state);
214 ScheduleUpdateState(duration); 166 state_updater_->ScheduleUpdateState(duration);
215 } 167 }
216 168
217 void MemoryCoordinatorImpl::Observe(int type, 169 void MemoryCoordinatorImpl::Observe(int type,
218 const NotificationSource& source, 170 const NotificationSource& source,
219 const NotificationDetails& details) { 171 const NotificationDetails& details) {
220 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); 172 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED);
221 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); 173 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr();
222 RenderProcessHost* process = render_widget_host->GetProcess(); 174 RenderProcessHost* process = render_widget_host->GetProcess();
223 if (!process) 175 if (!process)
224 return; 176 return;
225 auto iter = children().find(process->GetID()); 177 auto iter = children().find(process->GetID());
226 if (iter == children().end()) 178 if (iter == children().end())
227 return; 179 return;
228 iter->second.is_visible = *Details<bool>(details).ptr(); 180 iter->second.is_visible = *Details<bool>(details).ptr();
229 auto new_state = ToMojomMemoryState(GetGlobalMemoryState()); 181 auto new_state = ToMojomMemoryState(GetGlobalMemoryState());
230 SetChildMemoryState(iter->first, new_state); 182 SetChildMemoryState(iter->first, new_state);
231 } 183 }
232 184
233 bool MemoryCoordinatorImpl::ChangeStateIfNeeded(base::MemoryState prev_state, 185 bool MemoryCoordinatorImpl::ChangeStateIfNeeded(base::MemoryState prev_state,
234 base::MemoryState next_state) { 186 base::MemoryState next_state) {
187 DCHECK(CalledOnValidThread());
235 if (prev_state == next_state) 188 if (prev_state == next_state)
236 return false; 189 return false;
237 190
238 base::TimeTicks prev_last_state_change = last_state_change_; 191 base::TimeTicks prev_last_state_change = last_state_change_;
239 last_state_change_ = base::TimeTicks::Now(); 192 last_state_change_ = base::TimeTicks::Now();
240 current_state_ = next_state; 193 current_state_ = next_state;
241 194
242 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::ChangeStateIfNeeded", 195 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::ChangeStateIfNeeded",
243 "prev", MemoryStateToString(prev_state), 196 "prev", MemoryStateToString(prev_state),
244 "next", MemoryStateToString(next_state)); 197 "next", MemoryStateToString(next_state));
245 RecordStateChange(prev_state, next_state, 198 RecordStateChange(prev_state, next_state,
246 last_state_change_ - prev_last_state_change); 199 last_state_change_ - prev_last_state_change);
247 NotifyStateToClients(); 200 NotifyStateToClients();
248 NotifyStateToChildren(); 201 NotifyStateToChildren();
249 return true; 202 return true;
250 } 203 }
251 204
252 base::MemoryState MemoryCoordinatorImpl::CalculateNextState() {
253 using MemoryState = base::MemoryState;
254
255 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB();
256
257 // TODO(chrisha): Move this histogram recording to a better place when
258 // https://codereview.chromium.org/2479673002/ is landed.
259 UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.FreeMemoryUntilCritical",
260 available);
261
262 if (available <= 0)
263 return MemoryState::SUSPENDED;
264
265 int expected_renderer_count = available / expected_renderer_size_;
266
267 switch (current_state_) {
268 case MemoryState::NORMAL:
269 if (expected_renderer_count <= new_renderers_until_suspended_)
270 return MemoryState::SUSPENDED;
271 if (expected_renderer_count <= new_renderers_until_throttled_)
272 return MemoryState::THROTTLED;
273 return MemoryState::NORMAL;
274 case MemoryState::THROTTLED:
275 if (expected_renderer_count <= new_renderers_until_suspended_)
276 return MemoryState::SUSPENDED;
277 if (expected_renderer_count >= new_renderers_back_to_normal_)
278 return MemoryState::NORMAL;
279 return MemoryState::THROTTLED;
280 case MemoryState::SUSPENDED:
281 if (expected_renderer_count >= new_renderers_back_to_normal_)
282 return MemoryState::NORMAL;
283 if (expected_renderer_count >= new_renderers_back_to_throttled_)
284 return MemoryState::THROTTLED;
285 return MemoryState::SUSPENDED;
286 case MemoryState::UNKNOWN:
287 // Fall through
288 default:
289 NOTREACHED();
290 return MemoryState::UNKNOWN;
291 }
292 }
293
294 void MemoryCoordinatorImpl::UpdateState() {
295 MemoryState next_state = CalculateNextState();
296 if (ChangeStateIfNeeded(current_state_, next_state)) {
297 ScheduleUpdateState(minimum_transition_period_);
298 } else {
299 ScheduleUpdateState(monitoring_interval_);
300 }
301 }
302
303 void MemoryCoordinatorImpl::NotifyStateToClients() { 205 void MemoryCoordinatorImpl::NotifyStateToClients() {
304 auto state = GetCurrentMemoryState(); 206 auto state = GetCurrentMemoryState();
305 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); 207 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
306 } 208 }
307 209
308 void MemoryCoordinatorImpl::NotifyStateToChildren() { 210 void MemoryCoordinatorImpl::NotifyStateToChildren() {
309 auto mojo_state = ToMojomMemoryState(current_state_); 211 auto mojo_state = ToMojomMemoryState(current_state_);
310 // It's OK to call SetChildMemoryState() unconditionally because it checks 212 // It's OK to call SetChildMemoryState() unconditionally because it checks
311 // whether this state transition is valid. 213 // whether this state transition is valid.
312 for (auto& iter : children()) 214 for (auto& iter : children())
(...skipping 22 matching lines...) Expand all
335 render_process_host->GetHandle()); 237 render_process_host->GetHandle());
336 metrics->GetWorkingSetKBytes(&working_set); 238 metrics->GetWorkingSetKBytes(&working_set);
337 total_private_kb += working_set.priv; 239 total_private_kb += working_set.priv;
338 } 240 }
339 #endif 241 #endif
340 242
341 RecordMetricsOnStateChange(prev_state, next_state, duration, 243 RecordMetricsOnStateChange(prev_state, next_state, duration,
342 total_private_kb / 1024); 244 total_private_kb / 1024);
343 } 245 }
344 246
345 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) {
346 update_state_closure_.Reset(base::Bind(&MemoryCoordinatorImpl::UpdateState,
347 weak_ptr_factory_.GetWeakPtr()));
348 task_runner_->PostDelayedTask(FROM_HERE, update_state_closure_.callback(),
349 delta);
350 }
351
352 void MemoryCoordinatorImpl::InitializeParameters() {
353 expected_renderer_size_ = kDefaultExpectedRendererSizeMB;
354 new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled;
355 new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended;
356 new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal;
357 new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled;
358 minimum_transition_period_ =
359 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds);
360 monitoring_interval_ =
361 base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds);
362
363 // Override default parameters with variations.
364 static constexpr char kMemoryCoordinatorV0Trial[] = "MemoryCoordinatorV0";
365 std::map<std::string, std::string> params;
366 variations::GetVariationParams(kMemoryCoordinatorV0Trial, &params);
367 SetIntVariationParameter(params, "expected_renderer_size",
368 &expected_renderer_size_);
369 SetIntVariationParameter(params, "new_renderers_until_throttled",
370 &new_renderers_until_throttled_);
371 SetIntVariationParameter(params, "new_renderers_until_suspended",
372 &new_renderers_until_suspended_);
373 SetIntVariationParameter(params, "new_renderers_back_to_normal",
374 &new_renderers_back_to_normal_);
375 SetIntVariationParameter(params, "new_renderers_back_to_throttled",
376 &new_renderers_back_to_throttled_);
377 SetSecondsVariationParameter(params, "minimum_transition_period",
378 &minimum_transition_period_);
379 SetSecondsVariationParameter(params, "monitoring_interval",
380 &monitoring_interval_);
381 }
382
383 bool MemoryCoordinatorImpl::ValidateParameters() {
384 return (new_renderers_until_throttled_ > new_renderers_until_suspended_) &&
385 (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) &&
386 (new_renderers_back_to_normal_ > new_renderers_until_throttled_) &&
387 (new_renderers_back_to_throttled_ > new_renderers_until_suspended_);
388 }
389
390 } // namespace content 247 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/memory/memory_coordinator_impl.h ('k') | content/browser/memory/memory_coordinator_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698