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/crash_logging.h"
11 #include "base/debug/trace_event.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/synchronization/lock.h"
18 DiscardableMemoryManager::DiscardableMemoryManager(
20 size_t soft_memory_limit
,
21 TimeDelta hard_memory_limit_expiration_time
)
22 : allocations_(AllocationMap::NO_AUTO_EVICT
),
24 memory_limit_(memory_limit
),
25 soft_memory_limit_(soft_memory_limit
),
26 hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time
) {
27 BytesAllocatedChanged(bytes_allocated_
);
30 DiscardableMemoryManager::~DiscardableMemoryManager() {
31 DCHECK(allocations_
.empty());
32 DCHECK_EQ(0u, bytes_allocated_
);
35 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes
) {
37 memory_limit_
= bytes
;
38 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
39 Now(), memory_limit_
);
42 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes
) {
44 soft_memory_limit_
= bytes
;
47 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
48 TimeDelta hard_memory_limit_expiration_time
) {
50 hard_memory_limit_expiration_time_
= hard_memory_limit_expiration_time
;
53 bool DiscardableMemoryManager::ReduceMemoryUsage() {
54 return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
57 void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes
) {
59 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(),
63 void DiscardableMemoryManager::Register(Allocation
* allocation
, size_t bytes
) {
65 DCHECK(allocations_
.Peek(allocation
) == allocations_
.end());
66 allocations_
.Put(allocation
, AllocationInfo(bytes
));
69 void DiscardableMemoryManager::Unregister(Allocation
* allocation
) {
71 AllocationMap::iterator it
= allocations_
.Peek(allocation
);
72 DCHECK(it
!= allocations_
.end());
73 const AllocationInfo
& info
= it
->second
;
76 size_t bytes_purgable
= info
.bytes
;
77 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
78 bytes_allocated_
-= bytes_purgable
;
79 BytesAllocatedChanged(bytes_allocated_
);
81 allocations_
.Erase(it
);
84 bool DiscardableMemoryManager::AcquireLock(Allocation
* allocation
,
87 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
89 AllocationMap::iterator it
= allocations_
.Get(allocation
);
90 DCHECK(it
!= allocations_
.end());
91 AllocationInfo
* info
= &it
->second
;
96 TimeTicks now
= Now();
97 size_t bytes_required
= info
->purgable
? 0u : info
->bytes
;
101 if (bytes_required
< memory_limit_
)
102 limit
= memory_limit_
- bytes_required
;
104 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now
,
108 // Check for overflow.
109 if (std::numeric_limits
<size_t>::max() - bytes_required
< bytes_allocated_
)
112 *purged
= !allocation
->AllocateAndAcquireLock();
113 info
->purgable
= false;
114 info
->last_usage
= now
;
115 if (bytes_required
) {
116 bytes_allocated_
+= bytes_required
;
117 BytesAllocatedChanged(bytes_allocated_
);
122 void DiscardableMemoryManager::ReleaseLock(Allocation
* allocation
) {
123 AutoLock
lock(lock_
);
124 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
126 AllocationMap::iterator it
= allocations_
.Get(allocation
);
127 DCHECK(it
!= allocations_
.end());
128 AllocationInfo
* info
= &it
->second
;
130 TimeTicks now
= Now();
131 allocation
->ReleaseLock();
132 info
->purgable
= true;
133 info
->last_usage
= now
;
135 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
139 void DiscardableMemoryManager::PurgeAll() {
140 AutoLock
lock(lock_
);
141 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 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 bool DiscardableMemoryManager::
164 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
165 AutoLock
lock(lock_
);
167 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
168 Now() - hard_memory_limit_expiration_time_
, soft_memory_limit_
);
170 return bytes_allocated_
<= soft_memory_limit_
;
173 void DiscardableMemoryManager::
174 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
177 lock_
.AssertAcquired();
179 size_t bytes_allocated_before_purging
= bytes_allocated_
;
180 for (AllocationMap::reverse_iterator it
= allocations_
.rbegin();
181 it
!= allocations_
.rend();
183 Allocation
* allocation
= it
->first
;
184 AllocationInfo
* info
= &it
->second
;
186 if (bytes_allocated_
<= limit
)
189 bool purgable
= info
->purgable
&& info
->last_usage
<= timestamp
;
193 size_t bytes_purgable
= info
->bytes
;
194 DCHECK_LE(bytes_purgable
, bytes_allocated_
);
195 bytes_allocated_
-= bytes_purgable
;
196 info
->purgable
= false;
200 if (bytes_allocated_
!= bytes_allocated_before_purging
)
201 BytesAllocatedChanged(bytes_allocated_
);
204 void DiscardableMemoryManager::BytesAllocatedChanged(
205 size_t new_bytes_allocated
) const {
207 "base", "DiscardableMemoryUsage", this, new_bytes_allocated
);
209 static const char kDiscardableMemoryUsageKey
[] = "dm-usage";
210 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey
,
211 Uint64ToString(new_bytes_allocated
));
214 TimeTicks
DiscardableMemoryManager::Now() const {
215 return TimeTicks::Now();
218 } // namespace internal