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"
12 #include "base/sys_info.h"
19 // This is admittedly pretty magical. It's approximately enough memory for four
21 static const size_t kDefaultDiscardableMemoryLimit
= 64 * 1024 * 1024;
22 static const size_t kDefaultBytesToKeepUnderModeratePressure
=
23 kDefaultDiscardableMemoryLimit
/ 4;
27 DiscardableMemoryManager::DiscardableMemoryManager()
28 : allocations_(AllocationMap::NO_AUTO_EVICT
),
30 discardable_memory_limit_(kDefaultDiscardableMemoryLimit
),
31 bytes_to_keep_under_moderate_pressure_(
32 kDefaultBytesToKeepUnderModeratePressure
) {
33 BytesAllocatedChanged();
36 DiscardableMemoryManager::~DiscardableMemoryManager() {
37 DCHECK(allocations_
.empty());
38 DCHECK_EQ(0u, bytes_allocated_
);
41 void DiscardableMemoryManager::RegisterMemoryPressureListener() {
43 DCHECK(base::MessageLoop::current());
44 DCHECK(!memory_pressure_listener_
);
45 memory_pressure_listener_
.reset(
46 new MemoryPressureListener(
47 base::Bind(&DiscardableMemoryManager::OnMemoryPressure
,
51 void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
53 DCHECK(memory_pressure_listener_
);
54 memory_pressure_listener_
.reset();
57 void DiscardableMemoryManager::SetDiscardableMemoryLimit(size_t bytes
) {
59 discardable_memory_limit_
= bytes
;
60 EnforcePolicyWithLockAcquired();
63 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
66 bytes_to_keep_under_moderate_pressure_
= bytes
;
69 void DiscardableMemoryManager::Register(
70 const DiscardableMemory
* discardable
, size_t bytes
) {
72 // A registered memory listener is currently required. This DCHECK can be
73 // moved or removed if we decide that it's useful to relax this condition.
74 // TODO(reveman): Enable this DCHECK when skia and blink are able to
75 // register memory pressure listeners. crbug.com/333907
76 // DCHECK(memory_pressure_listener_);
77 DCHECK(allocations_
.Peek(discardable
) == allocations_
.end());
78 allocations_
.Put(discardable
, Allocation(bytes
));
81 void DiscardableMemoryManager::Unregister(
82 const DiscardableMemory
* discardable
) {
84 AllocationMap::iterator it
= allocations_
.Peek(discardable
);
85 if (it
== allocations_
.end())
88 if (it
->second
.memory
) {
89 size_t bytes
= it
->second
.bytes
;
90 DCHECK_LE(bytes
, bytes_allocated_
);
91 bytes_allocated_
-= bytes
;
92 BytesAllocatedChanged();
93 free(it
->second
.memory
);
95 allocations_
.Erase(it
);
98 scoped_ptr
<uint8
, FreeDeleter
> DiscardableMemoryManager::Acquire(
99 const DiscardableMemory
* discardable
,
101 AutoLock
lock(lock_
);
102 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
104 AllocationMap::iterator it
= allocations_
.Get(discardable
);
105 CHECK(it
!= allocations_
.end());
107 if (it
->second
.memory
) {
108 scoped_ptr
<uint8
, FreeDeleter
> memory(it
->second
.memory
);
109 it
->second
.memory
= NULL
;
111 return memory
.Pass();
114 size_t bytes
= it
->second
.bytes
;
116 return scoped_ptr
<uint8
, FreeDeleter
>();
118 if (discardable_memory_limit_
) {
120 if (bytes
< discardable_memory_limit_
)
121 limit
= discardable_memory_limit_
- bytes
;
123 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit
);
126 // Check for overflow.
127 if (std::numeric_limits
<size_t>::max() - bytes
< bytes_allocated_
)
128 return scoped_ptr
<uint8
, FreeDeleter
>();
130 scoped_ptr
<uint8
, FreeDeleter
> memory(static_cast<uint8
*>(malloc(bytes
)));
132 return scoped_ptr
<uint8
, FreeDeleter
>();
134 bytes_allocated_
+= bytes
;
135 BytesAllocatedChanged();
138 return memory
.Pass();
141 void DiscardableMemoryManager::Release(
142 const DiscardableMemory
* discardable
,
143 scoped_ptr
<uint8
, FreeDeleter
> memory
) {
144 AutoLock
lock(lock_
);
145 // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
147 AllocationMap::iterator it
= allocations_
.Get(discardable
);
148 CHECK(it
!= allocations_
.end());
150 DCHECK(!it
->second
.memory
);
151 it
->second
.memory
= memory
.release();
153 EnforcePolicyWithLockAcquired();
156 void DiscardableMemoryManager::PurgeAll() {
157 AutoLock
lock(lock_
);
158 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
161 bool DiscardableMemoryManager::IsRegisteredForTest(
162 const DiscardableMemory
* discardable
) const {
163 AutoLock
lock(lock_
);
164 AllocationMap::const_iterator it
= allocations_
.Peek(discardable
);
165 return it
!= allocations_
.end();
168 bool DiscardableMemoryManager::CanBePurgedForTest(
169 const DiscardableMemory
* discardable
) const {
170 AutoLock
lock(lock_
);
171 AllocationMap::const_iterator it
= allocations_
.Peek(discardable
);
172 return it
!= allocations_
.end() && it
->second
.memory
;
175 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
176 AutoLock
lock(lock_
);
177 return bytes_allocated_
;
180 void DiscardableMemoryManager::OnMemoryPressure(
181 MemoryPressureListener::MemoryPressureLevel pressure_level
) {
182 switch (pressure_level
) {
183 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE
:
186 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL
:
194 void DiscardableMemoryManager::Purge() {
195 AutoLock
lock(lock_
);
197 PurgeLRUWithLockAcquiredUntilUsageIsWithin(
198 bytes_to_keep_under_moderate_pressure_
);
201 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
205 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
208 lock_
.AssertAcquired();
210 size_t bytes_allocated_before_purging
= bytes_allocated_
;
211 for (AllocationMap::reverse_iterator it
= allocations_
.rbegin();
212 it
!= allocations_
.rend();
214 if (bytes_allocated_
<= limit
)
216 if (!it
->second
.memory
)
219 size_t bytes
= it
->second
.bytes
;
220 DCHECK_LE(bytes
, bytes_allocated_
);
221 bytes_allocated_
-= bytes
;
222 free(it
->second
.memory
);
223 it
->second
.memory
= NULL
;
226 if (bytes_allocated_
!= bytes_allocated_before_purging
)
227 BytesAllocatedChanged();
230 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() {
231 PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_
);
234 void DiscardableMemoryManager::BytesAllocatedChanged() const {
235 TRACE_COUNTER_ID1("base",
236 "DiscardableMemoryUsage",
241 } // namespace internal