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/adapters.h"
9 #include "base/containers/hash_tables.h"
10 #include "base/containers/mru_cache.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/synchronization/lock.h"
14 #include "base/trace_event/trace_event.h"
19 DiscardableMemoryManager::DiscardableMemoryManager(
21 size_t soft_memory_limit
,
22 TimeDelta hard_memory_limit_expiration_time
)
23 : allocations_(AllocationMap::NO_AUTO_EVICT
),
25 memory_limit_(memory_limit
),
26 soft_memory_limit_(soft_memory_limit
),
27 hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time
) {
28 BytesAllocatedChanged(bytes_allocated_
);
31 DiscardableMemoryManager::~DiscardableMemoryManager() {
32 DCHECK(allocations_
.empty());
33 DCHECK_EQ(0u, bytes_allocated_
);
36 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes
) {
38 memory_limit_
= bytes
;
39 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
40 Now(), memory_limit_
);
43 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes
) {
45 soft_memory_limit_
= bytes
;
48 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
49 TimeDelta hard_memory_limit_expiration_time
) {
51 hard_memory_limit_expiration_time_
= hard_memory_limit_expiration_time
;
54 bool DiscardableMemoryManager::ReduceMemoryUsage() {
55 return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
58 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes
) {
60 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
64 void DiscardableMemoryManager::Register(Allocation
* allocation
, size_t bytes
) {
66 DCHECK(allocations_
.Peek(allocation
) == allocations_
.end());
67 allocations_
.Put(allocation
, AllocationInfo(bytes
));
70 void DiscardableMemoryManager::Unregister(Allocation
* allocation
) {
72 AllocationMap::iterator it
= allocations_
.Peek(allocation
);
73 DCHECK(it
!= allocations_
.end());
74 const AllocationInfo
& info
= it
->second
;
77 size_t bytes_purgable
= info
.bytes
;
78 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
79 bytes_allocated_
-= bytes_purgable
;
80 BytesAllocatedChanged(bytes_allocated_
);
82 allocations_
.Erase(it
);
85 bool DiscardableMemoryManager::AcquireLock(Allocation
* allocation
,
88 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
90 AllocationMap::iterator it
= allocations_
.Get(allocation
);
91 DCHECK(it
!= allocations_
.end());
92 AllocationInfo
* info
= &it
->second
;
97 TimeTicks now
= Now();
98 size_t bytes_required
= info
->purgable
? 0u : info
->bytes
;
102 if (bytes_required
< memory_limit_
)
103 limit
= memory_limit_
- bytes_required
;
105 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now
,
109 // Check for overflow.
110 if (std::numeric_limits
<size_t>::max() - bytes_required
< bytes_allocated_
)
113 *purged
= !allocation
->AllocateAndAcquireLock();
114 info
->purgable
= false;
115 info
->last_usage
= now
;
116 if (bytes_required
) {
117 bytes_allocated_
+= bytes_required
;
118 BytesAllocatedChanged(bytes_allocated_
);
123 void DiscardableMemoryManager::ReleaseLock(Allocation
* allocation
) {
124 AutoLock
lock(lock_
);
125 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
127 AllocationMap::iterator it
= allocations_
.Get(allocation
);
128 DCHECK(it
!= allocations_
.end());
129 AllocationInfo
* info
= &it
->second
;
131 TimeTicks now
= Now();
132 allocation
->ReleaseLock();
133 info
->purgable
= true;
134 info
->last_usage
= now
;
136 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
140 void DiscardableMemoryManager::PurgeAll() {
141 AutoLock
lock(lock_
);
142 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
145 bool DiscardableMemoryManager::IsRegisteredForTest(
146 Allocation
* allocation
) const {
147 AutoLock
lock(lock_
);
148 AllocationMap::const_iterator it
= allocations_
.Peek(allocation
);
149 return it
!= allocations_
.end();
152 bool DiscardableMemoryManager::CanBePurgedForTest(
153 Allocation
* allocation
) const {
154 AutoLock
lock(lock_
);
155 AllocationMap::const_iterator it
= allocations_
.Peek(allocation
);
156 return it
!= allocations_
.end() && it
->second
.purgable
;
159 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
160 AutoLock
lock(lock_
);
161 return bytes_allocated_
;
164 bool DiscardableMemoryManager::
165 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
166 AutoLock
lock(lock_
);
168 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
169 Now() - hard_memory_limit_expiration_time_
, soft_memory_limit_
);
171 return bytes_allocated_
<= soft_memory_limit_
;
174 void DiscardableMemoryManager::
175 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
178 lock_
.AssertAcquired();
180 size_t bytes_allocated_before_purging
= bytes_allocated_
;
181 for (auto& entry
: base::Reversed(allocations_
)) {
182 Allocation
* allocation
= entry
.first
;
183 AllocationInfo
* info
= &entry
.second
;
185 if (bytes_allocated_
<= limit
)
188 bool purgable
= info
->purgable
&& info
->last_usage
<= timestamp
;
192 size_t bytes_purgable
= info
->bytes
;
193 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
194 bytes_allocated_
-= bytes_purgable
;
195 info
->purgable
= false;
199 if (bytes_allocated_
!= bytes_allocated_before_purging
)
200 BytesAllocatedChanged(bytes_allocated_
);
203 void DiscardableMemoryManager::BytesAllocatedChanged(
204 size_t new_bytes_allocated
) const {
206 "base", "DiscardableMemoryUsage", this, new_bytes_allocated
);
208 static const char kDiscardableMemoryUsageKey
[] = "dm-usage";
209 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey
,
210 Uint64ToString(new_bytes_allocated
));
213 TimeTicks
DiscardableMemoryManager::Now() const {
214 return TimeTicks::Now();
217 } // namespace internal