| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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) |
| 86 bytes = 0; |
| 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 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 if (render_process_host && render_process_host->GetHandle() == handle) | 289 if (render_process_host && render_process_host->GetHandle() == handle) |
| 279 return iter.second.memory_state; | 290 return iter.second.memory_state; |
| 280 } | 291 } |
| 281 return MemoryState::UNKNOWN; | 292 return MemoryState::UNKNOWN; |
| 282 } | 293 } |
| 283 | 294 |
| 284 void MemoryCoordinatorImpl::UpdateConditionIfNeeded( | 295 void MemoryCoordinatorImpl::UpdateConditionIfNeeded( |
| 285 MemoryCondition next_condition) { | 296 MemoryCondition next_condition) { |
| 286 DCHECK(CalledOnValidThread()); | 297 DCHECK(CalledOnValidThread()); |
| 287 | 298 |
| 288 // Discard one tab when the system is under high memory pressure. | 299 if (next_condition == MemoryCondition::WARNING) |
| 289 if (next_condition == MemoryCondition::CRITICAL) | 300 OnWarningCondition(); |
| 290 DiscardTab(); | 301 else if (next_condition == MemoryCondition::CRITICAL) |
| 302 OnCriticalCondition(); |
| 291 | 303 |
| 292 if (memory_condition_ == next_condition) | 304 if (memory_condition_ == next_condition) |
| 293 return; | 305 return; |
| 294 | 306 |
| 295 MemoryCondition prev_condition = memory_condition_; | 307 MemoryCondition prev_condition = memory_condition_; |
| 296 memory_condition_ = next_condition; | 308 memory_condition_ = next_condition; |
| 297 | 309 |
| 298 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"), | 310 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("memory_coordinator"), |
| 299 "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev", | 311 "MemoryCoordinatorImpl::UpdateConditionIfNeeded", "prev", |
| 300 MemoryConditionToString(prev_condition), "next", | 312 MemoryConditionToString(prev_condition), "next", |
| 301 MemoryConditionToString(next_condition)); | 313 MemoryConditionToString(next_condition)); |
| 302 | 314 |
| 303 // TODO(bashi): Following actions are tentative. We might want to prioritize | 315 // TODO(bashi): Following actions are tentative. We might want to prioritize |
| 304 // processes and handle them one-by-one. | 316 // processes and handle them one-by-one. |
| 305 | 317 |
| 306 if (next_condition == MemoryCondition::NORMAL) { | 318 if (next_condition == MemoryCondition::NORMAL) { |
| 307 // Set NORMAL state to all clients/processes. | 319 // Set NORMAL state to all clients/processes. |
| 308 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); | 320 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); |
| 309 NotifyStateToChildren(MemoryState::NORMAL); | 321 NotifyStateToChildren(MemoryState::NORMAL); |
| 310 } else if (next_condition == MemoryCondition::WARNING) { | 322 } else if (next_condition == MemoryCondition::WARNING) { |
| 311 // Set NORMAL state to foreground proceses and clients in the browser | 323 // Set NORMAL state to foreground proceses and clients in the browser |
| 312 // process. Set THROTTLED state to background processes. | 324 // process. Set THROTTLED state to background processes. |
| 313 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); | 325 UpdateBrowserStateAndNotifyStateToClients(MemoryState::NORMAL); |
| 314 for (auto& iter : children()) { | 326 for (auto& iter : children()) { |
| 315 auto state = | 327 auto state = |
| 316 iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; | 328 iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; |
| 317 SetChildMemoryState(iter.first, state); | 329 SetChildMemoryState(iter.first, state); |
| 318 } | 330 } |
| 319 // Idea: Purge memory from background processes. | |
| 320 } else if (next_condition == MemoryCondition::CRITICAL) { | 331 } else if (next_condition == MemoryCondition::CRITICAL) { |
| 321 // Set THROTTLED state to all clients/processes. | 332 // Set THROTTLED state to all clients/processes. |
| 322 UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED); | 333 UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED); |
| 323 NotifyStateToChildren(MemoryState::THROTTLED); | 334 NotifyStateToChildren(MemoryState::THROTTLED); |
| 324 } | 335 } |
| 325 } | 336 } |
| 326 | 337 |
| 327 void MemoryCoordinatorImpl::DiscardTab() { | 338 void MemoryCoordinatorImpl::DiscardTab() { |
| 328 if (delegate_) | 339 if (delegate_) |
| 329 delegate_->DiscardTab(); | 340 delegate_->DiscardTab(); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 !render_process_host->IsProcessBackgrounded()); | 397 !render_process_host->IsProcessBackgrounded()); |
| 387 } | 398 } |
| 388 | 399 |
| 389 void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id, | 400 void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id, |
| 390 bool is_visible) { | 401 bool is_visible) { |
| 391 auto iter = children().find(render_process_id); | 402 auto iter = children().find(render_process_id); |
| 392 if (iter == children().end()) | 403 if (iter == children().end()) |
| 393 return; | 404 return; |
| 394 | 405 |
| 395 iter->second.is_visible = is_visible; | 406 iter->second.is_visible = is_visible; |
| 407 if (!is_visible) { |
| 408 // A backgrounded process becomes a candidate for purging memory when |
| 409 // the process remains backgrounded for a certian period of time. |
| 410 iter->second.can_purge_after = |
| 411 tick_clock_->NowTicks() + |
| 412 base::TimeDelta::FromSeconds( |
| 413 kDefaultBackgroundChildPurgeCandidatePeriodSeconds); |
| 414 } |
| 396 MemoryState new_state = | 415 MemoryState new_state = |
| 397 CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible); | 416 CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible); |
| 398 SetChildMemoryState(render_process_id, new_state); | 417 SetChildMemoryState(render_process_id, new_state); |
| 399 } | 418 } |
| 400 | 419 |
| 401 MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state, | 420 MemoryState MemoryCoordinatorImpl::OverrideState(MemoryState memory_state, |
| 402 const ChildInfo& child) { | 421 const ChildInfo& child) { |
| 403 // We don't suspend foreground renderers. Throttle them instead. | 422 // We don't suspend foreground renderers. Throttle them instead. |
| 404 if (child.is_visible && memory_state == MemoryState::SUSPENDED) | 423 if (child.is_visible && memory_state == MemoryState::SUSPENDED) |
| 405 return MemoryState::THROTTLED; | 424 return MemoryState::THROTTLED; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); | 477 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
| 459 } | 478 } |
| 460 | 479 |
| 461 void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) { | 480 void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) { |
| 462 // It's OK to call SetChildMemoryState() unconditionally because it checks | 481 // It's OK to call SetChildMemoryState() unconditionally because it checks |
| 463 // whether this state transition is valid. | 482 // whether this state transition is valid. |
| 464 for (auto& iter : children()) | 483 for (auto& iter : children()) |
| 465 SetChildMemoryState(iter.first, state); | 484 SetChildMemoryState(iter.first, state); |
| 466 } | 485 } |
| 467 | 486 |
| 487 void MemoryCoordinatorImpl::OnWarningCondition() { |
| 488 TryToPurgeMemoryFromChildren(PurgeTarget::BACKGROUNDED); |
| 489 } |
| 490 |
| 491 void MemoryCoordinatorImpl::OnCriticalCondition() { |
| 492 DiscardTab(); |
| 493 |
| 494 // Prefer to purge memory from child processes than browser process because |
| 495 // we prioritize the brower process. |
| 496 if (TryToPurgeMemoryFromChildren(PurgeTarget::ALL)) |
| 497 return; |
| 498 |
| 499 TryToPurgeMemoryFromBrowser(); |
| 500 } |
| 501 |
| 502 bool MemoryCoordinatorImpl::TryToPurgeMemoryFromChildren(PurgeTarget target) { |
| 503 base::TimeTicks now = tick_clock_->NowTicks(); |
| 504 // TODO(bashi): Better to sort child processes based on their priorities. |
| 505 for (auto& iter : children()) { |
| 506 if (iter.second.is_visible && target == PurgeTarget::BACKGROUNDED) |
| 507 continue; |
| 508 if (!iter.second.can_purge_after.is_null() && |
| 509 iter.second.can_purge_after > now) |
| 510 continue; |
| 511 |
| 512 // Set |can_purge_after| to the maximum value to suppress another purge |
| 513 // request until the child process goes foreground and then goes background |
| 514 // again. |
| 515 iter.second.can_purge_after = base::TimeTicks::Max(); |
| 516 iter.second.handle->child()->PurgeMemory(); |
| 517 return true; |
| 518 } |
| 519 return false; |
| 520 } |
| 521 |
| 522 bool MemoryCoordinatorImpl::TryToPurgeMemoryFromBrowser() { |
| 523 base::TimeTicks now = tick_clock_->NowTicks(); |
| 524 if (can_purge_after_ > now) |
| 525 return false; |
| 526 |
| 527 auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics(); |
| 528 size_t before = metrics->GetWorkingSetSize(); |
| 529 task_runner_->PostDelayedTask(FROM_HERE, |
| 530 base::Bind(&RecordBrowserPurge, before), |
| 531 base::TimeDelta::FromSeconds(2)); |
| 532 |
| 533 // Suppress purging in the browser process until a certain period of time is |
| 534 // passed. |
| 535 can_purge_after_ = now + base::TimeDelta::FromMinutes(2); |
| 536 base::MemoryCoordinatorClientRegistry::GetInstance()->PurgeMemory(); |
| 537 return true; |
| 538 } |
| 539 |
| 468 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} | 540 MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} |
| 469 | 541 |
| 470 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { | 542 MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { |
| 471 // This is a nop, but exists for compatibility with STL containers. | 543 // This is a nop, but exists for compatibility with STL containers. |
| 472 } | 544 } |
| 473 | 545 |
| 474 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} | 546 MemoryCoordinatorImpl::ChildInfo::~ChildInfo() {} |
| 475 | 547 |
| 476 } // namespace content | 548 } // namespace content |
| OLD | NEW |