Skip to content

Commit

Permalink
Add OnPurgeMemory() to MemoryCoordinatorClient
Browse files Browse the repository at this point in the history
Before this CL, MemoryCoordinatorClient has only one callback called
OnMemoryStateChange(). Clients try to free up memory when state change
happens (e.g. NORMAL -> SUSPENDED). This may not be a good strategy on
some platforms because we may touch compressed pages (see [1] for details).

This CL add another callback called OnPurgeMemory() to separate logic
for purging existing memory from memory state changes. This way we can
build flexible strategies for handling memory pressure.

[1] https://groups.google.com/a/chromium.org/forum/?utm_medium=email&utm_source=footer#!msg/project-trim/s96xSirL2Hs/18uq1zfHEgAJ

BUG=684287

Review-Url: https://codereview.chromium.org/2655083003
Cr-Commit-Position: refs/heads/master@{#446951}
  • Loading branch information
bashi authored and Commit bot committed Jan 30, 2017
1 parent 7f6c6a0 commit a7f5cbb
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 18 deletions.
1 change: 1 addition & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,7 @@ test("base_unittests") {
"memory/aligned_memory_unittest.cc",
"memory/discardable_shared_memory_unittest.cc",
"memory/linked_ptr_unittest.cc",
"memory/memory_coordinator_client_registry_unittest.cc",
"memory/memory_pressure_listener_unittest.cc",
"memory/memory_pressure_monitor_chromeos_unittest.cc",
"memory/memory_pressure_monitor_mac_unittest.cc",
Expand Down
48 changes: 30 additions & 18 deletions base/memory/memory_coordinator_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,36 @@ namespace base {
// OVERVIEW:
//
// MemoryCoordinatorClient is an interface which a component can implement to
// respond to memory state changes. Unlike MemoryPressureListener, this is a
// stateful mechanism and clients receive notifications only when memory states
// are changed. State transitions are throttled to avoid thrashing; the exact
// throttling period is platform dependent, but will be at least 5-10 seconds.
// Clients are expected to make changes in memory usage that persist for the
// duration of the memory state.
// adjust "future allocation" and "existing allocation". For "future allocation"
// it provides a callback to observe memory state changes, and for "existing
// allocation" it provides a callback to purge memory.
//
// Unlike MemoryPressureListener, memory state changes are stateful. State
// transitions are throttled to avoid thrashing; the exact throttling period is
// platform dependent, but will be at least 5-10 seconds. When a state change
// notification is dispatched, clients are expected to update their allocation
// policies (e.g. setting cache limit) that persist for the duration of the
// memory state. Note that clients aren't expected to free up memory on memory
// state changes. Clients should wait for a separate purge request to free up
// memory. Purging requests will be throttled as well.

// MemoryState is an indicator that processes can use to guide their memory
// allocation policies. For example, a process that receives the suspended
// state can use that as as signal to drop memory caches.
// allocation policies. For example, a process that receives the throttled
// state can use that as as signal to decrease memory cache limits.
// NOTE: This enum is used to back an UMA histogram, and therefore should be
// treated as append-only.
enum class MemoryState : int {
// The state is unknown.
UNKNOWN = -1,
// No memory constraints.
NORMAL = 0,
// Running and interactive but allocation should be throttled.
// Clients should free up any memory that is used as an optimization but
// that is not necessary for the process to run (e.g. caches).
// Running and interactive but memory allocation should be throttled.
// Clients should set lower budget for any memory that is used as an
// optimization but that is not necessary for the process to run.
// (e.g. caches)
THROTTLED = 1,
// Still resident in memory but core processing logic has been suspended.
// Clients should free up any memory that is used as an optimization, or
// any memory whose contents can be reproduced when transitioning out of
// the suspended state (e.g. parsed resource that can be reloaded from disk).
// In most cases, OnPurgeMemory() will be called before entering this state.
SUSPENDED = 2,
};

Expand All @@ -54,11 +59,18 @@ class BASE_EXPORT MemoryCoordinatorClient {
// UNKNOWN. General guidelines are:
// * NORMAL: Restore the default settings for memory allocation/usage if
// it has changed.
// * THROTTLED: Use smaller limits for memory allocations and caches.
// * SUSPENDED: Purge memory.
virtual void OnMemoryStateChange(MemoryState state) = 0;
// * THROTTLED: Use smaller limits for future memory allocations. You don't
// need to take any action on existing allocations.
// * SUSPENDED: Use much smaller limits for future memory allocations. You
// don't need to take any action on existing allocations.
virtual void OnMemoryStateChange(MemoryState state) {}

// Called to purge memory.
// This callback should free up any memory that is used as an optimization, or
// any memory whose contents can be reproduced.
virtual void OnPurgeMemory() {}

protected:
protected:
virtual ~MemoryCoordinatorClient() {}
};

Expand Down
4 changes: 4 additions & 0 deletions base/memory/memory_coordinator_client_registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ void MemoryCoordinatorClientRegistry::Notify(MemoryState state) {
&base::MemoryCoordinatorClient::OnMemoryStateChange, state);
}

void MemoryCoordinatorClientRegistry::PurgeMemory() {
clients_->Notify(FROM_HERE, &base::MemoryCoordinatorClient::OnPurgeMemory);
}

} // namespace base
3 changes: 3 additions & 0 deletions base/memory/memory_coordinator_client_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class BASE_EXPORT MemoryCoordinatorClientRegistry {
// Notify clients of a memory state change.
void Notify(MemoryState state);

// Requests purging memory.
void PurgeMemory();

private:
friend struct DefaultSingletonTraits<MemoryCoordinatorClientRegistry>;

Expand Down
58 changes: 58 additions & 0 deletions base/memory/memory_coordinator_client_registry_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2017 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/memory/memory_coordinator_client_registry.h"

#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

namespace {

class TestMemoryCoordinatorClient : public MemoryCoordinatorClient {
public:
void OnMemoryStateChange(MemoryState state) override { state_ = state; }

void OnPurgeMemory() override { ++purge_count_; }

MemoryState state() const { return state_; }
size_t purge_count() const { return purge_count_; }

private:
MemoryState state_ = MemoryState::UNKNOWN;
size_t purge_count_ = 0;
};

void RunUntilIdle() {
base::RunLoop loop;
loop.RunUntilIdle();
}

TEST(MemoryCoordinatorClientRegistryTest, NotifyStateChange) {
MessageLoop loop;
auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
TestMemoryCoordinatorClient client;
registry->Register(&client);
registry->Notify(MemoryState::THROTTLED);
RunUntilIdle();
ASSERT_EQ(MemoryState::THROTTLED, client.state());
registry->Unregister(&client);
}

TEST(MemoryCoordinatorClientRegistryTest, PurgeMemory) {
MessageLoop loop;
auto* registry = MemoryCoordinatorClientRegistry::GetInstance();
TestMemoryCoordinatorClient client;
registry->Register(&client);
registry->PurgeMemory();
RunUntilIdle();
ASSERT_EQ(1u, client.purge_count());
registry->Unregister(&client);
}

} // namespace

} // namespace base

0 comments on commit a7f5cbb

Please sign in to comment.