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

Unified Diff: base/win/memory_pressure_monitor.cc

Issue 1122863005: Create base::win::MemoryPressureMonitor class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and pretty_print to make histograms.xml happy. Created 5 years, 7 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: base/win/memory_pressure_monitor.cc
diff --git a/base/win/memory_pressure_monitor.cc b/base/win/memory_pressure_monitor.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ed49a40a816b85c2df285624f2bdec55648ed9c5
--- /dev/null
+++ b/base/win/memory_pressure_monitor.cc
@@ -0,0 +1,254 @@
+// Copyright 2015 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 "base/win/memory_pressure_monitor.h"
+
+#include <windows.h>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+static const DWORDLONG kMBBytes = 1024 * 1024;
+
+// Enumeration of UMA memory pressure levels. This needs to be kept in sync with
+// histograms.xml and the memory pressure levels defined in
+// MemoryPressureListener.
+enum MemoryPressureLevelUMA {
+ UMA_MEMORY_PRESSURE_LEVEL_NONE = 0,
+ UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1,
+ UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
+ // This must be the last value in the enum.
+ UMA_MEMORY_PRESSURE_LEVEL_COUNT,
+};
+
+// Converts a memory pressure level to an UMA enumeration value.
+MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue(
+ MemoryPressureListener::MemoryPressureLevel level) {
+ switch (level) {
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+ return UMA_MEMORY_PRESSURE_LEVEL_NONE;
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+ return UMA_MEMORY_PRESSURE_LEVEL_MODERATE;
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+ return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL;
+ }
+ NOTREACHED();
+ return UMA_MEMORY_PRESSURE_LEVEL_NONE;
+}
+
+} // namespace
+
+// The following constants have been lifted from similar values in the ChromeOS
+// memory pressure monitor. The values were determined experimentally to ensure
+// sufficient responsiveness of the memory pressure subsystem, and minimal
+// overhead.
+const int MemoryPressureMonitor::kPollingIntervalMs = 5000;
+const int MemoryPressureMonitor::kModeratePressureCooldownMs = 10000;
+const int MemoryPressureMonitor::kModeratePressureCooldownCycles =
+ kModeratePressureCooldownMs / kPollingIntervalMs;
+
+// TODO(chrisha): Explore the following constants further with an experiment.
+
+// A system is considered 'high memory' if it has more than 1.5GB of system
+// memory available for use by the memory manager (not reserved for hardware
+// and drivers). This is a fuzzy version of the ~2GB discussed below.
+const int MemoryPressureMonitor::kLargeMemoryThresholdMb = 1536;
+
+// These are the default thresholds used for systems with < ~2GB of physical
+// memory. Such systems have been observed to always maintain ~100MB of
+// available memory, paging until that is the case. To try to avoid paging a
+// threshold slightly above this is chosen. The moderate threshold is slightly
+// less grounded in reality and chosen as 2.5x critical.
+const int MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb = 500;
+const int MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb = 200;
+
+// These are the default thresholds used for systems with >= ~2GB of physical
+// memory. Such systems have been observed to always maintain ~300MB of
+// available memory, paging until that is the case.
+const int MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb = 1000;
+const int MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb = 400;
+
+MemoryPressureMonitor::MemoryPressureMonitor()
+ : moderate_threshold_mb_(0),
+ critical_threshold_mb_(0),
+ current_memory_pressure_level_(
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+ moderate_pressure_repeat_count_(0),
+ weak_ptr_factory_(this) {
+ InferThresholds();
+ StartObserving();
+}
+
+MemoryPressureMonitor::MemoryPressureMonitor(int moderate_threshold_mb,
+ int critical_threshold_mb)
+ : moderate_threshold_mb_(moderate_threshold_mb),
+ critical_threshold_mb_(critical_threshold_mb),
+ current_memory_pressure_level_(
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+ moderate_pressure_repeat_count_(0),
+ weak_ptr_factory_(this) {
+ DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
+ DCHECK_LE(0, critical_threshold_mb_);
+ StartObserving();
+}
+
+MemoryPressureMonitor::~MemoryPressureMonitor() {
+ StopObserving();
+}
+
+void MemoryPressureMonitor::CheckMemoryPressureSoon() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+MemoryPressureListener::MemoryPressureLevel
+MemoryPressureMonitor::GetCurrentPressureLevel() const {
+ return current_memory_pressure_level_;
+}
+
+void MemoryPressureMonitor::InferThresholds() {
+ // Default to a 'high' memory situation, which uses more conservative
+ // thresholds.
+ bool high_memory = true;
+ MEMORYSTATUSEX mem_status = {};
+ if (GetSystemMemoryStatus(&mem_status)) {
+ static const DWORDLONG kLargeMemoryThresholdBytes =
+ static_cast<DWORDLONG>(kLargeMemoryThresholdMb) * kMBBytes;
+ high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes;
+ }
+
+ if (high_memory) {
+ moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb;
+ critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb;
+ } else {
+ moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb;
+ critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb;
+ }
+}
+
+void MemoryPressureMonitor::StartObserving() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ timer_.Start(FROM_HERE,
+ TimeDelta::FromMilliseconds(kPollingIntervalMs),
+ Bind(&MemoryPressureMonitor::
+ CheckMemoryPressureAndRecordStatistics,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void MemoryPressureMonitor::StopObserving() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // If StartObserving failed, StopObserving will still get called.
+ timer_.Stop();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void MemoryPressureMonitor::CheckMemoryPressure() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Get the previous pressure level and update the current one.
+ MemoryPressureLevel old_pressure = current_memory_pressure_level_;
+ current_memory_pressure_level_ = CalculateCurrentPressureLevel();
+
+ // |notify| will be set to true if MemoryPressureListeners need to be
+ // notified of a memory pressure level state change.
+ bool notify = false;
+ switch (current_memory_pressure_level_) {
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+ break;
+
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+ if (old_pressure != current_memory_pressure_level_) {
+ // This is a new transition to moderate pressure so notify.
+ moderate_pressure_repeat_count_ = 0;
+ notify = true;
+ } else {
+ // Already in moderate pressure, only notify if sustained over the
+ // cooldown period.
+ if (++moderate_pressure_repeat_count_ ==
+ kModeratePressureCooldownCycles) {
+ moderate_pressure_repeat_count_ = 0;
+ notify = true;
+ }
+ }
+ break;
+
+ case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+ // Always notify of critical pressure levels.
+ notify = true;
+ break;
+ }
+
+ if (!notify)
+ return;
+
+ // Emit a notification of the current memory pressure level. This can only
+ // happen for moderate and critical pressure levels.
+ DCHECK_NE(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ current_memory_pressure_level_);
+ MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_);
+}
+
+void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ CheckMemoryPressure();
+
+ UMA_HISTOGRAM_ENUMERATION(
+ "Memory.PressureLevel",
+ MemoryPressureLevelToUmaEnumValue(current_memory_pressure_level_),
+ UMA_MEMORY_PRESSURE_LEVEL_COUNT);
+}
+
+MemoryPressureListener::MemoryPressureLevel
+MemoryPressureMonitor::CalculateCurrentPressureLevel() {
+ MEMORYSTATUSEX mem_status = {};
+ if (!GetSystemMemoryStatus(&mem_status))
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+
+ // How much system memory is actively available for use right now, in MBs.
+ int phys_free = static_cast<int>(mem_status.ullAvailPhys / kMBBytes);
+
+ // TODO(chrisha): This should eventually care about address space pressure,
+ // but the browser process (where this is running) effectively never runs out
+ // of address space. Renderers occasionally do, but it does them no good to
+ // have the browser process monitor address space pressure. Long term,
+ // renderers should run their own address space pressure monitors and act
+ // accordingly, with the browser making cross-process decisions based on
+ // system memory pressure.
+
+ // Determine if the physical memory is under critical memory pressure.
+ if (phys_free <= critical_threshold_mb_)
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+
+ // Determine if the physical memory is under moderate memory pressure.
+ if (phys_free <= moderate_threshold_mb_)
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+
+ // No memory pressure was detected.
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+}
+
+bool MemoryPressureMonitor::GetSystemMemoryStatus(
+ MEMORYSTATUSEX* mem_status) {
+ DCHECK(mem_status != nullptr);
+ mem_status->dwLength = sizeof(*mem_status);
+ if (!::GlobalMemoryStatusEx(mem_status))
+ return false;
+ return true;
+}
+
+} // namespace win
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698