Chromium Code Reviews| Index: content/browser/memory/memory_coordinator_impl.cc |
| diff --git a/content/browser/memory/memory_coordinator_impl.cc b/content/browser/memory/memory_coordinator_impl.cc |
| index d5d25e6d407b6a505f7086f5c3cccf7258f8b0d0..e769d076a4362d56d49a8d3c8100e2df0297e29c 100644 |
| --- a/content/browser/memory/memory_coordinator_impl.cc |
| +++ b/content/browser/memory/memory_coordinator_impl.cc |
| @@ -28,6 +28,7 @@ using MemoryState = base::MemoryState; |
| namespace { |
| const int kDefaultMinimumTransitionPeriodSeconds = 30; |
| +const int kDefaultBackgroundChildPurgeCandidatePeriodSeconds = 30; |
| mojom::MemoryState ToMojomMemoryState(MemoryState state) { |
| switch (state) { |
| @@ -77,6 +78,16 @@ MemoryState CalculateMemoryStateForProcess(MemoryCondition condition, |
| return MemoryState::NORMAL; |
| } |
| +void RecordBrowserPurge(size_t before) { |
| + auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics(); |
| + size_t after = metrics->GetWorkingSetSize(); |
| + int64_t bytes = static_cast<int64_t>(before) - static_cast<int64_t>(after); |
| + 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.
|
| + return; |
| + UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Browser.PurgedMemory", |
| + bytes / 1024 / 1024); |
| +} |
| + |
| } // namespace |
| // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom |
| @@ -154,8 +165,10 @@ MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
| condition_observer_( |
| base::MakeUnique<MemoryConditionObserver>(this, task_runner)), |
| tick_clock_(base::MakeUnique<base::DefaultTickClock>()), |
| - minimum_state_transition_period_(base::TimeDelta::FromSeconds( |
| - kDefaultMinimumTransitionPeriodSeconds)) { |
| + minimum_state_transition_period_( |
| + base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds)), |
| + background_child_purge_candidate_period_(base::TimeDelta::FromSeconds( |
| + kDefaultBackgroundChildPurgeCandidatePeriodSeconds)) { |
| DCHECK(memory_monitor_.get()); |
| base::MemoryCoordinatorProxy::SetMemoryCoordinator(this); |
| @@ -285,9 +298,11 @@ void MemoryCoordinatorImpl::UpdateConditionIfNeeded( |
| MemoryCondition next_condition) { |
| DCHECK(CalledOnValidThread()); |
| - // Discard one tab when the system is under high memory pressure. |
| + if (next_condition == MemoryCondition::WARNING) |
| + OnWarningCondition(); |
| + |
| if (next_condition == MemoryCondition::CRITICAL) |
|
haraken
2017/03/24 11:55:39
else if
bashi
2017/03/28 03:15:31
Done.
|
| - DiscardTab(); |
| + OnCriticalCondition(); |
| if (memory_condition_ == next_condition) |
| return; |
| @@ -316,7 +331,6 @@ void MemoryCoordinatorImpl::UpdateConditionIfNeeded( |
| iter.second.is_visible ? MemoryState::NORMAL : MemoryState::THROTTLED; |
| SetChildMemoryState(iter.first, state); |
| } |
| - // Idea: Purge memory from background processes. |
| } else if (next_condition == MemoryCondition::CRITICAL) { |
| // Set THROTTLED state to all clients/processes. |
| UpdateBrowserStateAndNotifyStateToClients(MemoryState::THROTTLED); |
| @@ -324,9 +338,10 @@ void MemoryCoordinatorImpl::UpdateConditionIfNeeded( |
| } |
| } |
| -void MemoryCoordinatorImpl::DiscardTab() { |
| +bool MemoryCoordinatorImpl::DiscardTab() { |
| if (delegate_) |
| - delegate_->DiscardTab(); |
| + return delegate_->DiscardTab(); |
| + return false; |
| } |
| RenderProcessHost* MemoryCoordinatorImpl::GetRenderProcessHost( |
| @@ -393,6 +408,12 @@ void MemoryCoordinatorImpl::OnChildVisibilityChanged(int render_process_id, |
| return; |
| iter->second.is_visible = is_visible; |
| + if (!is_visible) { |
| + // A backgrounded process becomes a candidate for purging memory when |
| + // the process remains backgrounded for a certian period of time. |
| + iter->second.can_purge_after = |
| + 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.
|
| + } |
| MemoryState new_state = |
| CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible); |
| SetChildMemoryState(render_process_id, new_state); |
| @@ -465,6 +486,60 @@ void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) { |
| SetChildMemoryState(iter.first, state); |
| } |
| +void MemoryCoordinatorImpl::OnWarningCondition() { |
| + TryPurgeMemoryFromChildren(PurgeTarget::BACKGROUNDED); |
| +} |
| + |
| +void MemoryCoordinatorImpl::OnCriticalCondition() { |
| + // Prefer to discard a tab rather than purging memory on critical condition |
| + // to mitigate a risk to cause thrashing. |
| + if (DiscardTab()) |
| + return; |
| + |
| + 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.
|
| + return; |
| + |
| + TryPurgeMemoryFromBrowser(); |
| +} |
| + |
| +bool MemoryCoordinatorImpl::TryPurgeMemoryFromChildren(PurgeTarget target) { |
| + base::TimeTicks now = tick_clock_->NowTicks(); |
| + // TODO(bashi): Better to sort child processes based on their priorities. |
| + for (auto& iter : children()) { |
| + if (iter.second.is_visible && target == PurgeTarget::BACKGROUNDED) |
| + continue; |
| + 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
|
| + iter.second.can_purge_after > now) |
| + continue; |
| + |
| + // Set |can_purge_after| to the maximum value to suppress another purge |
| + // request until the child process goes foreground and then goes background |
| + // again. |
| + iter.second.can_purge_after = base::TimeTicks::Max(); |
| + iter.second.handle->child()->PurgeMemory(); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +bool MemoryCoordinatorImpl::TryPurgeMemoryFromBrowser() { |
| + base::TimeTicks now = tick_clock_->NowTicks(); |
| + 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.
|
| + return false; |
| + |
| + auto metrics = base::ProcessMetrics::CreateCurrentProcessMetrics(); |
| + size_t before = metrics->GetWorkingSetSize(); |
| + task_runner_->PostDelayedTask(FROM_HERE, |
| + base::Bind(&RecordBrowserPurge, before), |
| + base::TimeDelta::FromSeconds(2)); |
| + |
| + // Suppress purging in the browser process until a certain period of time is |
| + // passed. |
| + can_purge_after_ = now + base::TimeDelta::FromMinutes(2); |
| + base::MemoryCoordinatorClientRegistry::GetInstance()->PurgeMemory(); |
| + return true; |
| +} |
| + |
| MemoryCoordinatorImpl::ChildInfo::ChildInfo() {} |
| MemoryCoordinatorImpl::ChildInfo::ChildInfo(const ChildInfo& rhs) { |