| 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..f359f839a1982efb8efd6341aa3e0fab1ff12d64 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)
|
| + bytes = 0;
|
| + UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Browser.PurgedMemory",
|
| + bytes / 1024 / 1024);
|
| +}
|
| +
|
| } // namespace
|
|
|
| // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom
|
| @@ -285,9 +296,10 @@ void MemoryCoordinatorImpl::UpdateConditionIfNeeded(
|
| MemoryCondition next_condition) {
|
| DCHECK(CalledOnValidThread());
|
|
|
| - // Discard one tab when the system is under high memory pressure.
|
| - if (next_condition == MemoryCondition::CRITICAL)
|
| - DiscardTab();
|
| + if (next_condition == MemoryCondition::WARNING)
|
| + OnWarningCondition();
|
| + else if (next_condition == MemoryCondition::CRITICAL)
|
| + OnCriticalCondition();
|
|
|
| if (memory_condition_ == next_condition)
|
| return;
|
| @@ -316,7 +328,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);
|
| @@ -393,6 +404,14 @@ 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() +
|
| + base::TimeDelta::FromSeconds(
|
| + kDefaultBackgroundChildPurgeCandidatePeriodSeconds);
|
| + }
|
| MemoryState new_state =
|
| CalculateMemoryStateForProcess(GetMemoryCondition(), is_visible);
|
| SetChildMemoryState(render_process_id, new_state);
|
| @@ -465,6 +484,59 @@ void MemoryCoordinatorImpl::NotifyStateToChildren(MemoryState state) {
|
| SetChildMemoryState(iter.first, state);
|
| }
|
|
|
| +void MemoryCoordinatorImpl::OnWarningCondition() {
|
| + TryToPurgeMemoryFromChildren(PurgeTarget::BACKGROUNDED);
|
| +}
|
| +
|
| +void MemoryCoordinatorImpl::OnCriticalCondition() {
|
| + DiscardTab();
|
| +
|
| + // Prefer to purge memory from child processes than browser process because
|
| + // we prioritize the brower process.
|
| + if (TryToPurgeMemoryFromChildren(PurgeTarget::ALL))
|
| + return;
|
| +
|
| + TryToPurgeMemoryFromBrowser();
|
| +}
|
| +
|
| +bool MemoryCoordinatorImpl::TryToPurgeMemoryFromChildren(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() &&
|
| + 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::TryToPurgeMemoryFromBrowser() {
|
| + base::TimeTicks now = tick_clock_->NowTicks();
|
| + if (can_purge_after_ > now)
|
| + 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) {
|
|
|