OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/memory/discardable_memory_manager.h" | 5 #include "base/memory/discardable_memory_manager.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/containers/hash_tables.h" | 8 #include "base/containers/hash_tables.h" |
9 #include "base/containers/mru_cache.h" | 9 #include "base/containers/mru_cache.h" |
10 #include "base/debug/crash_logging.h" | 10 #include "base/debug/crash_logging.h" |
11 #include "base/debug/trace_event.h" | 11 #include "base/debug/trace_event.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "base/synchronization/lock.h" | 13 #include "base/synchronization/lock.h" |
14 | 14 |
15 namespace base { | 15 namespace base { |
16 namespace internal { | 16 namespace internal { |
17 | 17 |
18 DiscardableMemoryManager::DiscardableMemoryManager( | 18 DiscardableMemoryManager::DiscardableMemoryManager( |
19 size_t memory_limit, | 19 size_t memory_limit, |
20 size_t bytes_to_keep_under_moderate_pressure) | 20 size_t soft_memory_limit, |
| 21 size_t bytes_to_keep_under_moderate_pressure, |
| 22 TimeDelta hard_memory_limit_expiration_time) |
21 : allocations_(AllocationMap::NO_AUTO_EVICT), | 23 : allocations_(AllocationMap::NO_AUTO_EVICT), |
22 bytes_allocated_(0), | 24 bytes_allocated_(0u), |
23 memory_limit_(memory_limit), | 25 memory_limit_(memory_limit), |
| 26 soft_memory_limit_(soft_memory_limit), |
24 bytes_to_keep_under_moderate_pressure_( | 27 bytes_to_keep_under_moderate_pressure_( |
25 bytes_to_keep_under_moderate_pressure) { | 28 bytes_to_keep_under_moderate_pressure), |
26 BytesAllocatedChanged(); | 29 hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) { |
| 30 BytesAllocatedChanged(bytes_allocated_); |
27 } | 31 } |
28 | 32 |
29 DiscardableMemoryManager::~DiscardableMemoryManager() { | 33 DiscardableMemoryManager::~DiscardableMemoryManager() { |
30 DCHECK(allocations_.empty()); | 34 DCHECK(allocations_.empty()); |
31 DCHECK_EQ(0u, bytes_allocated_); | 35 DCHECK_EQ(0u, bytes_allocated_); |
32 } | 36 } |
33 | 37 |
34 void DiscardableMemoryManager::RegisterMemoryPressureListener() { | 38 void DiscardableMemoryManager::RegisterMemoryPressureListener() { |
35 AutoLock lock(lock_); | 39 AutoLock lock(lock_); |
36 DCHECK(base::MessageLoop::current()); | 40 DCHECK(base::MessageLoop::current()); |
37 DCHECK(!memory_pressure_listener_); | 41 DCHECK(!memory_pressure_listener_); |
38 memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( | 42 memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( |
39 &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); | 43 &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); |
40 } | 44 } |
41 | 45 |
42 void DiscardableMemoryManager::UnregisterMemoryPressureListener() { | 46 void DiscardableMemoryManager::UnregisterMemoryPressureListener() { |
43 AutoLock lock(lock_); | 47 AutoLock lock(lock_); |
44 DCHECK(memory_pressure_listener_); | 48 DCHECK(memory_pressure_listener_); |
45 memory_pressure_listener_.reset(); | 49 memory_pressure_listener_.reset(); |
46 } | 50 } |
47 | 51 |
48 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { | 52 void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { |
49 AutoLock lock(lock_); | 53 AutoLock lock(lock_); |
50 memory_limit_ = bytes; | 54 memory_limit_ = bytes; |
51 EnforcePolicyWithLockAcquired(); | 55 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 56 Now(), memory_limit_); |
| 57 } |
| 58 |
| 59 void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) { |
| 60 AutoLock lock(lock_); |
| 61 soft_memory_limit_ = bytes; |
52 } | 62 } |
53 | 63 |
54 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( | 64 void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( |
55 size_t bytes) { | 65 size_t bytes) { |
56 AutoLock lock(lock_); | 66 AutoLock lock(lock_); |
57 bytes_to_keep_under_moderate_pressure_ = bytes; | 67 bytes_to_keep_under_moderate_pressure_ = bytes; |
58 } | 68 } |
59 | 69 |
| 70 void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( |
| 71 TimeDelta hard_memory_limit_expiration_time) { |
| 72 AutoLock lock(lock_); |
| 73 hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time; |
| 74 } |
| 75 |
| 76 bool DiscardableMemoryManager::ReduceMemoryUsage() { |
| 77 return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); |
| 78 } |
| 79 |
60 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { | 80 void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { |
61 AutoLock lock(lock_); | 81 AutoLock lock(lock_); |
62 // A registered memory listener is currently required. This DCHECK can be | 82 // A registered memory listener is currently required. This DCHECK can be |
63 // moved or removed if we decide that it's useful to relax this condition. | 83 // moved or removed if we decide that it's useful to relax this condition. |
64 // TODO(reveman): Enable this DCHECK when skia and blink are able to | 84 // TODO(reveman): Enable this DCHECK when skia and blink are able to |
65 // register memory pressure listeners. crbug.com/333907 | 85 // register memory pressure listeners. crbug.com/333907 |
66 // DCHECK(memory_pressure_listener_); | 86 // DCHECK(memory_pressure_listener_); |
67 DCHECK(allocations_.Peek(allocation) == allocations_.end()); | 87 DCHECK(allocations_.Peek(allocation) == allocations_.end()); |
68 allocations_.Put(allocation, AllocationInfo(bytes)); | 88 allocations_.Put(allocation, AllocationInfo(bytes)); |
69 } | 89 } |
70 | 90 |
71 void DiscardableMemoryManager::Unregister(Allocation* allocation) { | 91 void DiscardableMemoryManager::Unregister(Allocation* allocation) { |
72 AutoLock lock(lock_); | 92 AutoLock lock(lock_); |
73 AllocationMap::iterator it = allocations_.Peek(allocation); | 93 AllocationMap::iterator it = allocations_.Peek(allocation); |
74 DCHECK(it != allocations_.end()); | 94 DCHECK(it != allocations_.end()); |
75 const AllocationInfo& info = it->second; | 95 const AllocationInfo& info = it->second; |
76 | 96 |
77 if (info.purgable) { | 97 if (info.purgable) { |
78 size_t bytes_purgable = info.bytes; | 98 size_t bytes_purgable = info.bytes; |
79 DCHECK_LE(bytes_purgable, bytes_allocated_); | 99 DCHECK_LE(bytes_purgable, bytes_allocated_); |
80 bytes_allocated_ -= bytes_purgable; | 100 bytes_allocated_ -= bytes_purgable; |
81 BytesAllocatedChanged(); | 101 BytesAllocatedChanged(bytes_allocated_); |
82 } | 102 } |
83 allocations_.Erase(it); | 103 allocations_.Erase(it); |
84 } | 104 } |
85 | 105 |
86 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation, | 106 bool DiscardableMemoryManager::AcquireLock(Allocation* allocation, |
87 bool* purged) { | 107 bool* purged) { |
88 AutoLock lock(lock_); | 108 AutoLock lock(lock_); |
89 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that | 109 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that |
90 // cache. | 110 // cache. |
91 AllocationMap::iterator it = allocations_.Get(allocation); | 111 AllocationMap::iterator it = allocations_.Get(allocation); |
92 DCHECK(it != allocations_.end()); | 112 DCHECK(it != allocations_.end()); |
93 AllocationInfo* info = &it->second; | 113 AllocationInfo* info = &it->second; |
94 | 114 |
95 if (!info->bytes) | 115 if (!info->bytes) |
96 return false; | 116 return false; |
97 | 117 |
| 118 TimeTicks now = Now(); |
98 size_t bytes_required = info->purgable ? 0u : info->bytes; | 119 size_t bytes_required = info->purgable ? 0u : info->bytes; |
99 | 120 |
100 if (memory_limit_) { | 121 if (memory_limit_) { |
101 size_t limit = 0; | 122 size_t limit = 0; |
102 if (bytes_required < memory_limit_) | 123 if (bytes_required < memory_limit_) |
103 limit = memory_limit_ - bytes_required; | 124 limit = memory_limit_ - bytes_required; |
104 | 125 |
105 PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); | 126 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now, |
| 127 limit); |
106 } | 128 } |
107 | 129 |
108 // Check for overflow. | 130 // Check for overflow. |
109 if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_) | 131 if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_) |
110 return false; | 132 return false; |
111 | 133 |
112 *purged = !allocation->AllocateAndAcquireLock(); | 134 *purged = !allocation->AllocateAndAcquireLock(); |
113 info->purgable = false; | 135 info->purgable = false; |
| 136 info->last_usage = now; |
114 if (bytes_required) { | 137 if (bytes_required) { |
115 bytes_allocated_ += bytes_required; | 138 bytes_allocated_ += bytes_required; |
116 BytesAllocatedChanged(); | 139 BytesAllocatedChanged(bytes_allocated_); |
117 } | 140 } |
118 return true; | 141 return true; |
119 } | 142 } |
120 | 143 |
121 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) { | 144 void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) { |
122 AutoLock lock(lock_); | 145 AutoLock lock(lock_); |
123 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that | 146 // Note: |allocations_| is an MRU cache, and use of |Get| here updates that |
124 // cache. | 147 // cache. |
125 AllocationMap::iterator it = allocations_.Get(allocation); | 148 AllocationMap::iterator it = allocations_.Get(allocation); |
126 DCHECK(it != allocations_.end()); | 149 DCHECK(it != allocations_.end()); |
127 AllocationInfo* info = &it->second; | 150 AllocationInfo* info = &it->second; |
128 | 151 |
| 152 TimeTicks now = Now(); |
129 allocation->ReleaseLock(); | 153 allocation->ReleaseLock(); |
130 info->purgable = true; | 154 info->purgable = true; |
131 EnforcePolicyWithLockAcquired(); | 155 info->last_usage = now; |
| 156 |
| 157 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 158 now, memory_limit_); |
132 } | 159 } |
133 | 160 |
134 void DiscardableMemoryManager::PurgeAll() { | 161 void DiscardableMemoryManager::PurgeAll() { |
135 AutoLock lock(lock_); | 162 AutoLock lock(lock_); |
136 PurgeLRUWithLockAcquiredUntilUsageIsWithin(0); | 163 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0); |
137 } | 164 } |
138 | 165 |
139 bool DiscardableMemoryManager::IsRegisteredForTest( | 166 bool DiscardableMemoryManager::IsRegisteredForTest( |
140 Allocation* allocation) const { | 167 Allocation* allocation) const { |
141 AutoLock lock(lock_); | 168 AutoLock lock(lock_); |
142 AllocationMap::const_iterator it = allocations_.Peek(allocation); | 169 AllocationMap::const_iterator it = allocations_.Peek(allocation); |
143 return it != allocations_.end(); | 170 return it != allocations_.end(); |
144 } | 171 } |
145 | 172 |
146 bool DiscardableMemoryManager::CanBePurgedForTest( | 173 bool DiscardableMemoryManager::CanBePurgedForTest( |
147 Allocation* allocation) const { | 174 Allocation* allocation) const { |
148 AutoLock lock(lock_); | 175 AutoLock lock(lock_); |
149 AllocationMap::const_iterator it = allocations_.Peek(allocation); | 176 AllocationMap::const_iterator it = allocations_.Peek(allocation); |
150 return it != allocations_.end() && it->second.purgable; | 177 return it != allocations_.end() && it->second.purgable; |
151 } | 178 } |
152 | 179 |
153 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { | 180 size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { |
154 AutoLock lock(lock_); | 181 AutoLock lock(lock_); |
155 return bytes_allocated_; | 182 return bytes_allocated_; |
156 } | 183 } |
157 | 184 |
158 void DiscardableMemoryManager::OnMemoryPressure( | 185 void DiscardableMemoryManager::OnMemoryPressure( |
159 MemoryPressureListener::MemoryPressureLevel pressure_level) { | 186 MemoryPressureListener::MemoryPressureLevel pressure_level) { |
160 switch (pressure_level) { | 187 switch (pressure_level) { |
161 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | 188 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: |
162 Purge(); | 189 PurgeUntilWithinBytesToKeepUnderModeratePressure(); |
163 return; | 190 return; |
164 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | 191 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: |
165 PurgeAll(); | 192 PurgeAll(); |
166 return; | 193 return; |
167 } | 194 } |
168 | 195 |
169 NOTREACHED(); | 196 NOTREACHED(); |
170 } | 197 } |
171 | 198 |
172 void DiscardableMemoryManager::Purge() { | 199 void |
| 200 DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { |
173 AutoLock lock(lock_); | 201 AutoLock lock(lock_); |
174 | 202 |
175 PurgeLRUWithLockAcquiredUntilUsageIsWithin( | 203 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
176 bytes_to_keep_under_moderate_pressure_); | 204 Now(), bytes_to_keep_under_moderate_pressure_); |
177 } | 205 } |
178 | 206 |
179 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( | 207 bool DiscardableMemoryManager:: |
180 size_t limit) { | 208 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { |
181 TRACE_EVENT1( | 209 AutoLock lock(lock_); |
182 "base", | |
183 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin", | |
184 "limit", | |
185 limit); | |
186 | 210 |
| 211 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 212 Now() - hard_memory_limit_expiration_time_, soft_memory_limit_); |
| 213 |
| 214 return bytes_allocated_ <= soft_memory_limit_; |
| 215 } |
| 216 |
| 217 void DiscardableMemoryManager:: |
| 218 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 219 TimeTicks timestamp, |
| 220 size_t limit) { |
187 lock_.AssertAcquired(); | 221 lock_.AssertAcquired(); |
188 | 222 |
189 size_t bytes_allocated_before_purging = bytes_allocated_; | 223 size_t bytes_allocated_before_purging = bytes_allocated_; |
190 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); | 224 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); |
191 it != allocations_.rend(); | 225 it != allocations_.rend(); |
192 ++it) { | 226 ++it) { |
193 Allocation* allocation = it->first; | 227 Allocation* allocation = it->first; |
194 AllocationInfo* info = &it->second; | 228 AllocationInfo* info = &it->second; |
195 | 229 |
196 if (bytes_allocated_ <= limit) | 230 if (bytes_allocated_ <= limit) |
197 break; | 231 break; |
198 if (!info->purgable) | 232 |
| 233 bool purgable = info->purgable && info->last_usage <= timestamp; |
| 234 if (!purgable) |
199 continue; | 235 continue; |
200 | 236 |
201 size_t bytes_purgable = info->bytes; | 237 size_t bytes_purgable = info->bytes; |
202 DCHECK_LE(bytes_purgable, bytes_allocated_); | 238 DCHECK_LE(bytes_purgable, bytes_allocated_); |
203 bytes_allocated_ -= bytes_purgable; | 239 bytes_allocated_ -= bytes_purgable; |
204 info->purgable = false; | 240 info->purgable = false; |
205 allocation->Purge(); | 241 allocation->Purge(); |
206 } | 242 } |
207 | 243 |
208 if (bytes_allocated_ != bytes_allocated_before_purging) | 244 if (bytes_allocated_ != bytes_allocated_before_purging) |
209 BytesAllocatedChanged(); | 245 BytesAllocatedChanged(bytes_allocated_); |
210 } | 246 } |
211 | 247 |
212 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() { | 248 void DiscardableMemoryManager::BytesAllocatedChanged( |
213 PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_); | 249 size_t new_bytes_allocated) const { |
214 } | 250 TRACE_COUNTER_ID1( |
215 | 251 "base", "DiscardableMemoryUsage", this, new_bytes_allocated); |
216 void DiscardableMemoryManager::BytesAllocatedChanged() const { | |
217 TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_); | |
218 | 252 |
219 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; | 253 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; |
220 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, | 254 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, |
221 Uint64ToString(bytes_allocated_)); | 255 Uint64ToString(new_bytes_allocated)); |
| 256 } |
| 257 |
| 258 TimeTicks DiscardableMemoryManager::Now() const { |
| 259 return TimeTicks::Now(); |
222 } | 260 } |
223 | 261 |
224 } // namespace internal | 262 } // namespace internal |
225 } // namespace base | 263 } // namespace base |
OLD | NEW |