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

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

Issue 2763933002: memory coordinator: Purge memory under memory pressure (Closed)
Patch Set: Use int64_t Created 3 years, 9 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
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/memory/memory_coordinator_client_registry.h" 7 #include "base/memory/memory_coordinator_client_registry.h"
8 #include "base/memory/ptr_util.h" 8 #include "base/memory/ptr_util.h"
9 #include "base/metrics/histogram_macros.h" 9 #include "base/metrics/histogram_macros.h"
10 #include "base/process/process_handle.h" 10 #include "base/process/process_handle.h"
(...skipping 10 matching lines...) Expand all
21 #include "content/public/common/content_features.h" 21 #include "content/public/common/content_features.h"
22 #include "mojo/public/cpp/bindings/binding.h" 22 #include "mojo/public/cpp/bindings/binding.h"
23 23
24 namespace content { 24 namespace content {
25 25
26 using MemoryState = base::MemoryState; 26 using MemoryState = base::MemoryState;
27 27
28 namespace { 28 namespace {
29 29
30 const int kDefaultMinimumTransitionPeriodSeconds = 30; 30 const int kDefaultMinimumTransitionPeriodSeconds = 30;
31 const int kDefaultBackgroundChildPurgeCandidatePeriodSeconds = 30;
31 32
32 mojom::MemoryState ToMojomMemoryState(MemoryState state) { 33 mojom::MemoryState ToMojomMemoryState(MemoryState state) {
33 switch (state) { 34 switch (state) {
34 case MemoryState::UNKNOWN: 35 case MemoryState::UNKNOWN:
35 return mojom::MemoryState::UNKNOWN; 36 return mojom::MemoryState::UNKNOWN;
36 case MemoryState::NORMAL: 37 case MemoryState::NORMAL:
37 return mojom::MemoryState::NORMAL; 38 return mojom::MemoryState::NORMAL;
38 case MemoryState::THROTTLED: 39 case MemoryState::THROTTLED:
39 return mojom::MemoryState::THROTTLED; 40 return mojom::MemoryState::THROTTLED;
40 case MemoryState::SUSPENDED: 41 case MemoryState::SUSPENDED:
(...skipping 29 matching lines...) Expand all
70 return MemoryState::NORMAL; 71 return MemoryState::NORMAL;
71 case MemoryCondition::WARNING: 72 case MemoryCondition::WARNING:
72 return is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; 73 return is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED;
73 case MemoryCondition::CRITICAL: 74 case MemoryCondition::CRITICAL:
74 return MemoryState::THROTTLED; 75 return MemoryState::THROTTLED;
75 } 76 }
76 NOTREACHED(); 77 NOTREACHED();
77 return MemoryState::NORMAL; 78 return MemoryState::NORMAL;
78 } 79 }
79 80
81 void RecordBrowserPurge(size_t before) {
82 auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
83 size_t after = metrics->GetWorkingSetSize();
84 int64_t bytes = static_cast<int64_t>(before) - static_cast<int64_t>(after);
85 if (bytes < 0)
haraken 2017/03/24 11:55:39 Maybe we want to record this case too. See my comm
bashi 2017/03/28 03:15:31 Acknowledged.
86 return;
87 UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Browser.PurgedMemory",
88 bytes / 1024 / 1024);
89 }
90
80 } // namespace 91 } // namespace
81 92
82 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom 93 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom
83 // for the role of this class. 94 // for the role of this class.
84 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle { 95 class MemoryCoordinatorHandleImpl : public mojom::MemoryCoordinatorHandle {
85 public: 96 public:
86 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request, 97 MemoryCoordinatorHandleImpl(mojom::MemoryCoordinatorHandleRequest request,
87 MemoryCoordinatorImpl* coordinator, 98 MemoryCoordinatorImpl* coordinator,
88 int render_process_id); 99 int render_process_id);
89 ~MemoryCoordinatorHandleImpl() override; 100 ~MemoryCoordinatorHandleImpl() override;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 158
148 MemoryCoordinatorImpl::MemoryCoordinatorImpl( 159 MemoryCoordinatorImpl::MemoryCoordinatorImpl(
149 scoped_refptr<base::SingleThreadTaskRunner> task_runner, 160 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
150 std::unique_ptr<MemoryMonitor> memory_monitor) 161 std::unique_ptr<MemoryMonitor> memory_monitor)
151 : task_runner_(task_runner), 162 : task_runner_(task_runner),
152 delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()), 163 delegate_(GetContentClient()->browser()->GetMemoryCoordinatorDelegate()),
153 memory_monitor_(std::move(memory_monitor)), 164 memory_monitor_(std::move(memory_monitor)),
154 condition_observer_( 165 condition_observer_(
155 base::MakeUnique<MemoryConditionObserver>(this, task_runner)), 166 base::MakeUnique<MemoryConditionObserver>(this, task_runner)),
156 tick_clock_(base::MakeUnique<base::DefaultTickClock>()), 167 tick_clock_(base::MakeUnique<base::DefaultTickClock>()),
157 minimum_state_transition_period_(base::TimeDelta::FromSeconds( 168 minimum_state_transition_period_(
158 kDefaultMinimumTransitionPeriodSeconds)) { 169 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds)),
170 background_child_purge_candidate_period_(base::TimeDelta::FromSeconds(
171 kDefaultBackgroundChildPurgeCandidatePeriodSeconds)) {
159 DCHECK(memory_monitor_.get()); 172 DCHECK(memory_monitor_.get());
160 base::MemoryCoordinatorProxy::SetMemoryCoordinator(this); 173 base::MemoryCoordinatorProxy::SetMemoryCoordinator(this);
161 174
162 // Force the "memory_coordinator" category to show up in the trace viewer. 175 // Force the "memory_coordinator" category to show up in the trace viewer.
163 base::trace_event::TraceLog::GetCategoryGroupEnabled( 176 base::trace_event::TraceLog::GetCategoryGroupEnabled(
164 TRACE_DISABLED_BY_DEFAULT("memory_coordinator")); 177 TRACE_DISABLED_BY_DEFAULT("memory_coordinator"));
165 } 178 }
166 179
167 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() { 180 MemoryCoordinatorImpl::~MemoryCoordinatorImpl() {
168 base::MemoryCoordinatorProxy::SetMemoryCoordinator(nullptr); 181 base::MemoryCoordinatorProxy::SetMemoryCoordinator(nullptr);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 if (render_process_host && render_process_host->GetHandle() == handle) 291 if (render_process_host && render_process_host->GetHandle() == handle)
279 return iter.second.memory_state; 292 return iter.second.memory_state;
280 } 293 }
281 return MemoryState::UNKNOWN; 294 return MemoryState::UNKNOWN;
282 } 295 }
283 296
284 void MemoryCoordinatorImpl::UpdateConditionIfNeeded( 297 void MemoryCoordinatorImpl::UpdateConditionIfNeeded(
285 MemoryCondition next_condition) { 298 MemoryCondition next_condition) {
286 DCHECK(CalledOnValidThread()); 299 DCHECK(CalledOnValidThread());
287 300
288 // Discard one tab when the system is under high memory pressure. 301 if (next_condition == MemoryCondition::WARNING)
302 OnWarningCondition();
303
289 if (next_condition == MemoryCondition::CRITICAL) 304 if (next_condition == MemoryCondition::CRITICAL)
haraken 2017/03/24 11:55:39 else if
bashi 2017/03/28 03:15:31 Done.
290 DiscardTab(); 305 OnCriticalCondition();
291 306
292 if (memory_condition_ == next_condition) 307 if (memory_condition_ == next_condition)
293 return; 308 return;
294 309
295 MemoryCondition prev_condition = memory_condition_; 310 MemoryCondition prev_condition = memory_condition_;
296 memory_condition_ = next_condition; 311 memory_condition_ = next_condition;
297 312
298 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"), 313 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"),
299 "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev", 314 "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev",
300 MemoryConditionToString(prev_condition), "next", 315 MemoryConditionToString(prev_condition), "next",
301 MemoryConditionToString(next_condition)); 316 MemoryConditionToString(next_condition));
302 317
303 // TODO(bashi): Following actions are tentative. We might want to prioritize 318 // TODO(bashi): Following actions are tentative. We might want to prioritize
304 // processes and handle them one-by-one. 319 // processes and handle them one-by-one.
305 320
306 if (next_condition == MemoryCondition::NORMAL) { 321 if (next_condition == MemoryCondition::NORMAL) {
307 // Set NORMAL state to all clients/processes. 322 // Set NORMAL state to all clients/processes.
308 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); 323 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL);
309 NotifyStateToChildren(MemoryState::NORMAL); 324 NotifyStateToChildren(MemoryState::NORMAL);
310 } else if (next_condition == MemoryCondition::WARNING) { 325 } else if (next_condition == MemoryCondition::WARNING) {
311 // Set NORMAL state to foreground proceses and clients in the browser 326 // Set NORMAL state to foreground proceses and clients in the browser
312 // process. Set THROTTLED state to background processes. 327 // process. Set THROTTLED state to background processes.
313 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); 328 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL);
314 for (auto& iter : children()) { 329 for (auto& iter : children()) {
315 auto state = 330 auto state =
316 iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; 331 iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED;
317 SetChildMemoryState(iter.first, state); 332 SetChildMemoryState(iter.first, state);
318 } 333 }
319 // Idea: Purge memory from background processes.
320 } else if (next_condition == MemoryCondition::CRITICAL) { 334 } else if (next_condition == MemoryCondition::CRITICAL) {
321 // Set THROTTLED state to all clients/processes. 335 // Set THROTTLED state to all clients/processes.
322 UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED); 336 UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED);
323 NotifyStateToChildren(MemoryState::THROTTLED); 337 NotifyStateToChildren(MemoryState::THROTTLED);
324 } 338 }
325 } 339 }
326 340
327 void MemoryCoordinatorImpl::DiscardTab() { 341 bool MemoryCoordinatorImpl::DiscardTab() {
328 if (delegate_) 342 if (delegate_)
329 delegate_->DiscardTab(); 343 return delegate_->DiscardTab();
344 return false;
330 } 345 }
331 346
332 RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost( 347 RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost(
333 int render_process_id) { 348 int render_process_id) {
334 return RenderProcessHost::FromID(render_process_id); 349 return RenderProcessHost::FromID(render_process_id);
335 } 350 }
336 351
337 void MemoryCoordinatorImpl::SetDelegateForTesting( 352 void MemoryCoordinatorImpl::SetDelegateForTesting(
338 std::unique_ptr<MemoryCoordinatorDelegate> delegate) { 353 std::unique_ptr<MemoryCoordinatorDelegate> delegate) {
339 CHECK(!delegate_); 354 CHECK(!delegate_);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 !render_process_host->IsProcessBackgrounded()); 401 !render_process_host->IsProcessBackgrounded());
387 } 402 }
388 403
389 void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id, 404 void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id,
390 bool is_visible) { 405 bool is_visible) {
391 auto iter = children().find(render_process_id); 406 auto iter = children().find(render_process_id);
392 if (iter == children().end()) 407 if (iter == children().end())
393 return; 408 return;
394 409
395 iter->second.is_visible = is_visible; 410 iter->second.is_visible = is_visible;
411 if (!is_visible) {
412 // A backgrounded process becomes a candidate for purging memory when
413 // the process remains backgrounded for a certian period of time.
414 iter->second.can_purge_after =
415 tick_clock_->NowTicks() + background_child_purge_candidate_period_;
haraken 2017/03/24 11:55:39 Maybe you can inline base::TimeDelta::FromSeconds(
bashi 2017/03/28 03:15:31 Done.
416 }
396 MemoryState new_state = 417 MemoryState new_state =
397 CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible); 418 CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible);
398 SetChildMemoryState(render_process_id, new_state); 419 SetChildMemoryState(render_process_id, new_state);
399 } 420 }
400 421
401 MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state, 422 MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state,
402 const ChildInfo& child) { 423 const ChildInfo& child) {
403 // We don't suspend foreground renderers. Throttle them instead. 424 // We don't suspend foreground renderers. Throttle them instead.
404 if (child.is_visible && memory_state == MemoryState::SUSPENDED) 425 if (child.is_visible && memory_state == MemoryState::SUSPENDED)
405 return MemoryState::THROTTLED; 426 return MemoryState::THROTTLED;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); 479 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state);
459 } 480 }
460 481
461 void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) { 482 void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) {
462 // It's OK to call SetChildMemoryState() unconditionally because it checks 483 // It's OK to call SetChildMemoryState() unconditionally because it checks
463 // whether this state transition is valid. 484 // whether this state transition is valid.
464 for (auto& iter : children()) 485 for (auto& iter : children())
465 SetChildMemoryState(iter.first, state); 486 SetChildMemoryState(iter.first, state);
466 } 487 }
467 488
489 void MemoryCoordinatorImpl::OnWarningCondition() {
490 TryPurgeMemoryFromChildren(PurgeTarget::BACKGROUNDED);
491 }
492
493 void MemoryCoordinatorImpl::OnCriticalCondition() {
494 // Prefer to discard a tab rather than purging memory on critical condition
495 // to mitigate a risk to cause thrashing.
496 if (DiscardTab())
497 return;
498
499 if (TryPurgeMemoryFromChildren(PurgeTarget::ALL))
haraken 2017/03/24 11:55:39 Add: // Prefer purging memory from child proces
bashi 2017/03/28 03:15:31 Done.
500 return;
501
502 TryPurgeMemoryFromBrowser();
503 }
504
505 bool MemoryCoordinatorImpl::TryPurgeMemoryFromChildren(PurgeTarget target) {
506 base::TimeTicks now = tick_clock_->NowTicks();
507 // TODO(bashi): Better to sort child processes based on their priorities.
508 for (auto& iter : children()) {
509 if (iter.second.is_visible && target == PurgeTarget::BACKGROUNDED)
510 continue;
511 if (!iter.second.can_purge_after.is_null() &&
haraken 2017/03/24 11:55:38 I'd prefer initializing can_purge_after to base::T
bashi 2017/03/28 03:15:31 Using Max() as the initial value is slightly diffe
512 iter.second.can_purge_after > now)
513 continue;
514
515 // Set |can_purge_after| to the maximum value to suppress another purge
516 // request until the child process goes foreground and then goes background
517 // again.
518 iter.second.can_purge_after = base::TimeTicks::Max();
519 iter.second.handle->child()->PurgeMemory();
520 return true;
521 }
522 return false;
523 }
524
525 bool MemoryCoordinatorImpl::TryPurgeMemoryFromBrowser() {
526 base::TimeTicks now = tick_clock_->NowTicks();
527 if (!can_purge_after_.is_null() && can_purge_after_ > now)
haraken 2017/03/24 11:55:38 Remove the null check.
bashi 2017/03/28 03:15:31 Done.
528 return false;
529
530 auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
531 size_t before = metrics->GetWorkingSetSize();
532 task_runner_->PostDelayedTask(FROM_HERE,
533 base::Bind(&RecordBrowserPurge, before),
534 base::TimeDelta::FromSeconds(2));
535
536 // Suppress purging in the browser process until a certain period of time is
537 // passed.
538 can_purge_after_ = now + base::TimeDelta::FromMinutes(2);
539 base::MemoryCoordinatorClientRegistry::GetInstance()->PurgeMemory();
540 return true;
541 }
542
468 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} 543 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {}
469 544
470 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { 545 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) {
471 // This is a nop, but exists for compatibility with STL containers. 546 // This is a nop, but exists for compatibility with STL containers.
472 } 547 }
473 548
474 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} 549 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {}
475 550
476 } // namespace content 551 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698