| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/memory/memory_state_updater.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "components/variations/variations_associated_data.h" |
| #include "content/browser/memory/memory_monitor.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // A expected renderer size. These values come from the median of appropriate |
| // UMA stats. |
| #if defined(OS_ANDROID) || defined(OS_IOS) |
| const int kDefaultExpectedRendererSizeMB = 40; |
| #elif defined(OS_WIN) |
| const int kDefaultExpectedRendererSizeMB = 70; |
| #else // Mac, Linux, and ChromeOS |
| const int kDefaultExpectedRendererSizeMB = 120; |
| #endif |
| |
| // Default values for parameters to determine the global state. |
| const int kDefaultNewRenderersUntilThrottled = 4; |
| const int kDefaultNewRenderersUntilSuspended = 2; |
| const int kDefaultNewRenderersBackToNormal = 5; |
| const int kDefaultNewRenderersBackToThrottled = 3; |
| const int kDefaultMinimumTransitionPeriodSeconds = 30; |
| const int kDefaultMonitoringIntervalSeconds = 5; |
| |
| void SetIntVariationParameter(const std::map<std::string, std::string> params, |
| const char* name, |
| int* target) { |
| const auto& iter = params.find(name); |
| if (iter == params.end()) |
| return; |
| int value; |
| if (!iter->second.empty() && base::StringToInt(iter->second, &value)) { |
| DCHECK(value > 0); |
| *target = value; |
| } |
| } |
| |
| void SetSecondsVariationParameter( |
| const std::map<std::string, std::string> params, |
| const char* name, |
| base::TimeDelta* target) { |
| const auto& iter = params.find(name); |
| if (iter == params.end()) |
| return; |
| int value; |
| if (!iter->second.empty() && base::StringToInt(iter->second, &value)) { |
| DCHECK(value > 0); |
| *target = base::TimeDelta::FromSeconds(value); |
| } |
| } |
| |
| } // namespace |
| |
| MemoryStateUpdater::MemoryStateUpdater( |
| MemoryCoordinatorImpl* coordinator, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : coordinator_(coordinator), task_runner_(task_runner) { |
| DCHECK(coordinator_); |
| InitializeParameters(); |
| DCHECK(ValidateParameters()); |
| } |
| |
| MemoryStateUpdater::~MemoryStateUpdater() {} |
| |
| base::MemoryState MemoryStateUpdater::CalculateNextState() { |
| using MemoryState = base::MemoryState; |
| |
| int available = |
| coordinator_->memory_monitor()->GetFreeMemoryUntilCriticalMB(); |
| |
| // TODO(chrisha): Move this histogram recording to a better place when |
| // https://codereview.chromium.org/2479673002/ is landed. |
| UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Coordinator.FreeMemoryUntilCritical", |
| available); |
| |
| if (available <= 0) |
| return MemoryState::SUSPENDED; |
| |
| auto current_state = coordinator_->GetGlobalMemoryState(); |
| int expected_renderer_count = available / expected_renderer_size_; |
| |
| switch (current_state) { |
| case MemoryState::NORMAL: |
| if (expected_renderer_count <= new_renderers_until_suspended_) |
| return MemoryState::SUSPENDED; |
| if (expected_renderer_count <= new_renderers_until_throttled_) |
| return MemoryState::THROTTLED; |
| return MemoryState::NORMAL; |
| case MemoryState::THROTTLED: |
| if (expected_renderer_count <= new_renderers_until_suspended_) |
| return MemoryState::SUSPENDED; |
| if (expected_renderer_count >= new_renderers_back_to_normal_) |
| return MemoryState::NORMAL; |
| return MemoryState::THROTTLED; |
| case MemoryState::SUSPENDED: |
| if (expected_renderer_count >= new_renderers_back_to_normal_) |
| return MemoryState::NORMAL; |
| if (expected_renderer_count >= new_renderers_back_to_throttled_) |
| return MemoryState::THROTTLED; |
| return MemoryState::SUSPENDED; |
| case MemoryState::UNKNOWN: |
| // Fall through |
| default: |
| NOTREACHED(); |
| return MemoryState::UNKNOWN; |
| } |
| } |
| |
| void MemoryStateUpdater::UpdateState() { |
| auto current_state = coordinator_->GetGlobalMemoryState(); |
| auto next_state = CalculateNextState(); |
| if (coordinator_->ChangeStateIfNeeded(current_state, next_state)) { |
| ScheduleUpdateState(minimum_transition_period_); |
| } else { |
| ScheduleUpdateState(monitoring_interval_); |
| } |
| } |
| |
| void MemoryStateUpdater::ScheduleUpdateState(base::TimeDelta delta) { |
| update_state_closure_.Reset(base::Bind(&MemoryStateUpdater::UpdateState, |
| base::Unretained(this))); |
| task_runner_->PostDelayedTask(FROM_HERE, update_state_closure_.callback(), |
| delta); |
| } |
| |
| void MemoryStateUpdater::InitializeParameters() { |
| expected_renderer_size_ = kDefaultExpectedRendererSizeMB; |
| new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled; |
| new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended; |
| new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal; |
| new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled; |
| minimum_transition_period_ = |
| base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds); |
| monitoring_interval_ = |
| base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds); |
| |
| // Override default parameters with variations. |
| static constexpr char kMemoryCoordinatorV0Trial[] = "MemoryCoordinatorV0"; |
| std::map<std::string, std::string> params; |
| variations::GetVariationParams(kMemoryCoordinatorV0Trial, ¶ms); |
| SetIntVariationParameter(params, "expected_renderer_size", |
| &expected_renderer_size_); |
| SetIntVariationParameter(params, "new_renderers_until_throttled", |
| &new_renderers_until_throttled_); |
| SetIntVariationParameter(params, "new_renderers_until_suspended", |
| &new_renderers_until_suspended_); |
| SetIntVariationParameter(params, "new_renderers_back_to_normal", |
| &new_renderers_back_to_normal_); |
| SetIntVariationParameter(params, "new_renderers_back_to_throttled", |
| &new_renderers_back_to_throttled_); |
| SetSecondsVariationParameter(params, "minimum_transition_period", |
| &minimum_transition_period_); |
| SetSecondsVariationParameter(params, "monitoring_interval", |
| &monitoring_interval_); |
| } |
| |
| bool MemoryStateUpdater::ValidateParameters() { |
| return (new_renderers_until_throttled_ > new_renderers_until_suspended_) && |
| (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) && |
| (new_renderers_back_to_normal_ > new_renderers_until_throttled_) && |
| (new_renderers_back_to_throttled_ > new_renderers_until_suspended_); |
| } |
| |
| } // namespace content |