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

Unified 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 side-by-side diff with in-line comments
Download patch
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) {

Powered by Google App Engine
This is Rietveld 408576698