1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/memory/discardable_memory_manager.h"
8 #include "base/containers/hash_tables.h"
9 #include "base/containers/mru_cache.h"
10 #include "base/debug/trace_event.h"
11 #include "base/synchronization/lock.h"
17 // This is admittedly pretty magical. It's approximately enough memory for four
19 static const size_t kDefaultMemoryLimit
= 64 * 1024 * 1024;
20 static const size_t kDefaultBytesToKeepUnderModeratePressure
=
21 kDefaultMemoryLimit
/ 4;
25 DiscardableMemoryManager::DiscardableMemoryManager()
26 : allocations_(AllocationMap::NO_AUTO_EVICT
),
28 memory_limit_(kDefaultMemoryLimit
),
29 bytes_to_keep_under_moderate_pressure_(
30 kDefaultBytesToKeepUnderModeratePressure
) {
31 BytesAllocatedChanged();
34 DiscardableMemoryManager::~DiscardableMemoryManager() {
35 DCHECK(allocations_
.empty());
36 DCHECK_EQ(0u, bytes_allocated_
);
39 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
41 DCHECK(base::MessageLoop::current());
42 DCHECK(!memory_pressure_listener_
);
43 memory_pressure_listener_
.reset(new MemoryPressureListener(base::Bind(
44 &DiscardableMemoryManager::OnMemoryPressure
, Unretained(this))));
47 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
49 DCHECK(memory_pressure_listener_
);
50 memory_pressure_listener_
.reset();
53 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes
) {
55 memory_limit_
= bytes
;
56 EnforcePolicyWithLockAcquired();
59 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
62 bytes_to_keep_under_moderate_pressure_
= bytes
;
65 void DiscardableMemoryManager::Register(Allocation
* allocation
, size_t bytes
) {
67 // A registered memory listener is currently required. This DCHECK can be
68 // moved or removed if we decide that it's useful to relax this condition.
69 // TODO(reveman): Enable this DCHECK when skia and blink are able to
70 // register memory pressure listeners. crbug.com/333907
71 // DCHECK(memory_pressure_listener_);
72 DCHECK(allocations_
.Peek(allocation
) == allocations_
.end());
73 allocations_
.Put(allocation
, AllocationInfo(bytes
));
76 void DiscardableMemoryManager::Unregister(Allocation
* allocation
) {
78 AllocationMap::iterator it
= allocations_
.Peek(allocation
);
79 DCHECK(it
!= allocations_
.end());
80 const AllocationInfo
& info
= it
->second
;
83 size_t bytes_purgable
= info
.bytes
;
84 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
85 bytes_allocated_
-= bytes_purgable
;
86 BytesAllocatedChanged();
88 allocations_
.Erase(it
);
91 bool DiscardableMemoryManager::AcquireLock(Allocation
* allocation
,
94 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
96 AllocationMap::iterator it
= allocations_
.Get(allocation
);
97 DCHECK(it
!= allocations_
.end());
98 AllocationInfo
* info
= &it
->second
;
103 size_t bytes_required
= info
->purgable
? 0u : info
->bytes
;
107 if (bytes_required
< memory_limit_
)
108 limit
= memory_limit_
- bytes_required
;
110 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit
);
113 // Check for overflow.
114 if (std::numeric_limits
<size_t>::max() - bytes_required
< bytes_allocated_
)
117 *purged
= !allocation
->AllocateAndAcquireLock(info
->bytes
);
118 info
->purgable
= false;
119 if (bytes_required
) {
120 bytes_allocated_
+= bytes_required
;
121 BytesAllocatedChanged();
126 void DiscardableMemoryManager::ReleaseLock(Allocation
* allocation
) {
127 AutoLock
lock(lock_
);
128 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
130 AllocationMap::iterator it
= allocations_
.Get(allocation
);
131 DCHECK(it
!= allocations_
.end());
132 AllocationInfo
* info
= &it
->second
;
134 allocation
->ReleaseLock();
135 info
->purgable
= true;
136 EnforcePolicyWithLockAcquired();
139 void DiscardableMemoryManager::PurgeAll() {
140 AutoLock
lock(lock_
);
141 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
144 bool DiscardableMemoryManager::IsRegisteredForTest(
145 Allocation
* allocation
) const {
146 AutoLock
lock(lock_
);
147 AllocationMap::const_iterator it
= allocations_
.Peek(allocation
);
148 return it
!= allocations_
.end();
151 bool DiscardableMemoryManager::CanBePurgedForTest(
152 Allocation
* allocation
) const {
153 AutoLock
lock(lock_
);
154 AllocationMap::const_iterator it
= allocations_
.Peek(allocation
);
155 return it
!= allocations_
.end() && it
->second
.purgable
;
158 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
159 AutoLock
lock(lock_
);
160 return bytes_allocated_
;
163 void DiscardableMemoryManager::OnMemoryPressure(
164 MemoryPressureListener::MemoryPressureLevel pressure_level
) {
165 switch (pressure_level
) {
166 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE
:
169 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL
:
177 void DiscardableMemoryManager::Purge() {
178 AutoLock
lock(lock_
);
180 PurgeLRUWithLockAcquiredUntilUsageIsWithin(
181 bytes_to_keep_under_moderate_pressure_
);
184 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
188 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
192 lock_
.AssertAcquired();
194 size_t bytes_allocated_before_purging
= bytes_allocated_
;
195 for (AllocationMap::reverse_iterator it
= allocations_
.rbegin();
196 it
!= allocations_
.rend();
198 Allocation
* allocation
= it
->first
;
199 AllocationInfo
* info
= &it
->second
;
201 if (bytes_allocated_
<= limit
)
206 size_t bytes_purgable
= info
->bytes
;
207 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
208 bytes_allocated_
-= bytes_purgable
;
209 info
->purgable
= false;
213 if (bytes_allocated_
!= bytes_allocated_before_purging
)
214 BytesAllocatedChanged();
217 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
218 PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_
);
221 void DiscardableMemoryManager::BytesAllocatedChanged() const {
222 TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_
);
225 } // namespace internal