Commit 1be61ae0 authored by chrisha's avatar chrisha Committed by Commit bot

Create MemoryMonitor and Windows implementation.

This defines the interface for the memory monitoring component of the MemoryCoordinator, and provides an implementation on Windows.

BUG=617492

Review-Url: https://codereview.chromium.org/2236983002
Cr-Commit-Position: refs/heads/master@{#412627}
parent 1a86f6d6
......@@ -6,6 +6,9 @@ static_library("browser") {
sources = [
"memory_coordinator.cc",
"memory_coordinator.h",
"memory_monitor.h",
"memory_monitor_win.cc",
"memory_monitor_win.h",
]
deps = [
......@@ -23,6 +26,7 @@ source_set("unit_tests") {
sources = [
"memory_coordinator_unittest.cc",
"memory_monitor_win_unittest.cc",
]
deps = [
......
// Copyright (c) 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.
#ifndef COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_H_
#define COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_H_
#include <memory>
#include "base/macros.h"
namespace memory_coordinator {
// A simple class that monitors the amount of free memory available on a system.
// This is an interface to facilitate dependency injection for testing.
class MemoryMonitor {
public:
MemoryMonitor() {}
virtual ~MemoryMonitor() {}
// Returns the amount of free memory available on the system until the system
// will be in a critical state. Critical is as defined by the OS (swapping
// will occur, or physical memory will run out, etc). It is possible for this
// to return negative values, in which case that much memory would have to be
// freed in order to exit a critical memory state.
virtual int GetFreeMemoryUntilCriticalMB() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(MemoryMonitor);
};
// Factory function for creating a monitor for the current platform.
std::unique_ptr<MemoryMonitor> CreateMemoryMonitor();
} // namespace memory_coordinator
#endif // COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_H_
// Copyright (c) 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 "components/memory_coordinator/browser/memory_monitor_win.h"
#include "base/process/process_metrics.h"
// TODO(chrisha): Implement a mechanism for observing swapping, and updating the
// memory threshold on a per machine basis.
namespace memory_coordinator {
namespace {
const int kKBperMB = 1024;
// A default implementation of MemoryMonitorWinDelegate. Used by default by
// MemoryMonitorWin.
class MemoryMonitorWinDelegateImpl : public MemoryMonitorWinDelegate {
public:
MemoryMonitorWinDelegateImpl() {}
~MemoryMonitorWinDelegateImpl() override {}
// GetSystemMemoryInfoDelegate:
void GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) override {
base::GetSystemMemoryInfo(mem_info);
}
private:
DISALLOW_COPY_AND_ASSIGN(MemoryMonitorWinDelegateImpl);
};
// A global static instance of the default delegate. Used by default by
// MemoryMonitorWin.
MemoryMonitorWinDelegateImpl g_memory_monitor_win_delegate;
} // namespace
// A system is considered 'large 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 MemoryMonitorWin::kLargeMemoryThresholdMB = 1536;
// This is the target free memory 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.
const int MemoryMonitorWin::kSmallMemoryTargetFreeMB = 200;
// This is the target free memory 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 MemoryMonitorWin::kLargeMemoryTargetFreeMB = 400;
MemoryMonitorWin::MemoryMonitorWin(MemoryMonitorWinDelegate* delegate,
int target_free_mb)
: delegate_(delegate), target_free_mb_(target_free_mb) {
}
int MemoryMonitorWin::GetFreeMemoryUntilCriticalMB() {
base::SystemMemoryInfoKB mem_info = {};
delegate_->GetSystemMemoryInfo(&mem_info);
int free_mb = mem_info.free / kKBperMB;
free_mb -= target_free_mb_;
return free_mb;
}
// static
std::unique_ptr<MemoryMonitorWin> MemoryMonitorWin::Create(
MemoryMonitorWinDelegate* delegate) {
return std::unique_ptr<MemoryMonitorWin>(new MemoryMonitorWin(
delegate, GetTargetFreeMB(delegate)));
}
// static
bool MemoryMonitorWin::IsLargeMemory(MemoryMonitorWinDelegate* delegate) {
base::SystemMemoryInfoKB mem_info = {};
delegate->GetSystemMemoryInfo(&mem_info);
return (mem_info.total / kKBperMB) >=
MemoryMonitorWin::kLargeMemoryThresholdMB;
}
// static
int MemoryMonitorWin::GetTargetFreeMB(MemoryMonitorWinDelegate* delegate) {
if (IsLargeMemory(delegate))
return MemoryMonitorWin::kLargeMemoryTargetFreeMB;
return MemoryMonitorWin::kSmallMemoryTargetFreeMB;
}
// Implementation of factory function defined in memory_monitor.h.
std::unique_ptr<MemoryMonitor> CreateMemoryMonitor() {
return MemoryMonitorWin::Create(&g_memory_monitor_win_delegate);
}
} // namespace memory_coordinator
// Copyright (c) 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.
#ifndef COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_WIN_H_
#define COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_WIN_H_
#include "components/memory_coordinator/browser/memory_monitor.h"
namespace base {
struct SystemMemoryInfoKB;
} // namespace base
namespace memory_coordinator {
class MemoryMonitorWinDelegate;
// A memory monitor for the Windows platform. After much experimentation this
// class uses a very simple heuristic to anticipate paging (critical memory
// pressure). When the amount of memory available dips below a provided
// threshold, it is assumed that paging is inevitable.
class MemoryMonitorWin : public MemoryMonitor {
public:
// Default constants governing the amount of free memory that the memory
// manager attempts to maintain.
static const int kLargeMemoryThresholdMB;
static const int kSmallMemoryTargetFreeMB;
static const int kLargeMemoryTargetFreeMB;
MemoryMonitorWin(MemoryMonitorWinDelegate* delegate, int target_free_mb);
~MemoryMonitorWin() override {}
// MemoryMonitor:
int GetFreeMemoryUntilCriticalMB() override;
// Returns the current free memory target.
int target_free_mb() const { return target_free_mb_; }
// Factory function. Automatically sizes |target_free_mb| based on the
// system.
static std::unique_ptr<MemoryMonitorWin> Create(
MemoryMonitorWinDelegate* delegate);
protected:
// Determines if the system is in large memory mode. Exposed so that this
// function can be tested.
static bool IsLargeMemory(MemoryMonitorWinDelegate* delegate);
// Determines the default target free MB value. Exposed so that this function
// can be tested.
static int GetTargetFreeMB(MemoryMonitorWinDelegate* delegate);
private:
// The delegate to be used for retrieving system memory information. Used as a
// testing seam.
MemoryMonitorWinDelegate* delegate_;
// The amount of memory that the memory manager (MM) attempts to keep in a
// free state. When less than this amount of physical memory is free, it is
// assumed that the MM will start paging things out.
int target_free_mb_;
};
// A delegate that wraps functions used by MemoryMonitorWin. Used as a testing
// seam.
class MemoryMonitorWinDelegate {
public:
MemoryMonitorWinDelegate() {}
virtual ~MemoryMonitorWinDelegate() {}
// Returns system memory information.
virtual void GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(MemoryMonitorWinDelegate);
};
} // namespace memory_coordinator
#endif // COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_MONITOR_WIN_H_
// Copyright (c) 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 "components/memory_coordinator/browser/memory_monitor_win.h"
#include "base/process/process_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_coordinator {
namespace {
// A delegate that allows mocking the various inputs to MemoryMonitorWin.
class TestMemoryMonitorWinDelegate : public MemoryMonitorWinDelegate {
public:
TestMemoryMonitorWinDelegate() : calls_(0) {
mem_info_ = {};
}
~TestMemoryMonitorWinDelegate() override {}
// GetSystemMemoryInfoDelegate:
void GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) override {
*mem_info = mem_info_;
++calls_;
}
size_t calls() const { return calls_; }
void ResetCalls() { calls_ = 0; }
void SetTotalMemoryKB(int total_memory_kb) {
mem_info_.total = total_memory_kb;
}
void SetFreeMemoryKB(int free_memory_kb) {
mem_info_.free = free_memory_kb;
}
private:
size_t calls_;
base::SystemMemoryInfoKB mem_info_;
DISALLOW_COPY_AND_ASSIGN(TestMemoryMonitorWinDelegate);
};
class TestMemoryMonitorWin : public MemoryMonitorWin {
public:
using MemoryMonitorWin::IsLargeMemory;
using MemoryMonitorWin::GetTargetFreeMB;
};
static const int kKBperMB = 1024;
} // namespace
class MemoryMonitorWinTest : public testing::Test {
public:
TestMemoryMonitorWinDelegate delegate_;
std::unique_ptr<MemoryMonitorWin> monitor_;
};
TEST_F(MemoryMonitorWinTest, IsLargeMemory) {
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB - 1);
EXPECT_FALSE(TestMemoryMonitorWin::IsLargeMemory(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB);
EXPECT_TRUE(TestMemoryMonitorWin::IsLargeMemory(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB + 100);
EXPECT_TRUE(TestMemoryMonitorWin::IsLargeMemory(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
}
TEST_F(MemoryMonitorWinTest, GetTargetFreeMB) {
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB - 1);
EXPECT_EQ(MemoryMonitorWin::kSmallMemoryTargetFreeMB,
TestMemoryMonitorWin::GetTargetFreeMB(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB);
EXPECT_EQ(MemoryMonitorWin::kLargeMemoryTargetFreeMB,
TestMemoryMonitorWin::GetTargetFreeMB(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB + 100);
EXPECT_EQ(MemoryMonitorWin::kLargeMemoryTargetFreeMB,
TestMemoryMonitorWin::GetTargetFreeMB(&delegate_));
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
}
TEST_F(MemoryMonitorWinTest, Create) {
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB - 1);
monitor_ = MemoryMonitorWin::Create(&delegate_);
EXPECT_EQ(MemoryMonitorWin::kSmallMemoryTargetFreeMB,
monitor_->target_free_mb());
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB);
monitor_ = MemoryMonitorWin::Create(&delegate_);
EXPECT_EQ(MemoryMonitorWin::kLargeMemoryTargetFreeMB,
monitor_->target_free_mb());
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetTotalMemoryKB(
MemoryMonitorWin::kLargeMemoryThresholdMB * kKBperMB + 100);
monitor_ = MemoryMonitorWin::Create(&delegate_);
EXPECT_EQ(MemoryMonitorWin::kLargeMemoryTargetFreeMB,
monitor_->target_free_mb());
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
}
TEST_F(MemoryMonitorWinTest, Constructor) {
monitor_.reset(new MemoryMonitorWin(&delegate_, 100));
EXPECT_EQ(100, monitor_->target_free_mb());
EXPECT_EQ(0u, delegate_.calls());
monitor_.reset(new MemoryMonitorWin(&delegate_, 387));
EXPECT_EQ(387, monitor_->target_free_mb());
EXPECT_EQ(0u, delegate_.calls());
}
TEST_F(MemoryMonitorWinTest, GetFreeMemoryUntilCriticalMB) {
monitor_.reset(new MemoryMonitorWin(&delegate_, 100));
EXPECT_EQ(100, monitor_->target_free_mb());
EXPECT_EQ(0u, delegate_.calls());
delegate_.SetFreeMemoryKB(200 * kKBperMB);
EXPECT_EQ(100, monitor_->GetFreeMemoryUntilCriticalMB());
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
delegate_.SetFreeMemoryKB(50 * kKBperMB);
EXPECT_EQ(-50, monitor_->GetFreeMemoryUntilCriticalMB());
EXPECT_EQ(1u, delegate_.calls());
delegate_.ResetCalls();
}
} // namespace memory_coordinator
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment