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::IdleNotification() { |
| 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 |
| 185 TimeTicks DiscardableMemoryManager::Now() const { |
| 186 return TimeTicks::Now(); |
| 187 } |
| 188 |
158 void DiscardableMemoryManager::OnMemoryPressure( | 189 void DiscardableMemoryManager::OnMemoryPressure( |
159 MemoryPressureListener::MemoryPressureLevel pressure_level) { | 190 MemoryPressureListener::MemoryPressureLevel pressure_level) { |
160 switch (pressure_level) { | 191 switch (pressure_level) { |
161 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: | 192 case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: |
162 Purge(); | 193 PurgeUntilWithinBytesToKeepUnderModeratePressure(); |
163 return; | 194 return; |
164 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: | 195 case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: |
165 PurgeAll(); | 196 PurgeAll(); |
166 return; | 197 return; |
167 } | 198 } |
168 | 199 |
169 NOTREACHED(); | 200 NOTREACHED(); |
170 } | 201 } |
171 | 202 |
172 void DiscardableMemoryManager::Purge() { | 203 void |
| 204 DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { |
173 AutoLock lock(lock_); | 205 AutoLock lock(lock_); |
174 | 206 |
175 PurgeLRUWithLockAcquiredUntilUsageIsWithin( | 207 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
176 bytes_to_keep_under_moderate_pressure_); | 208 Now(), bytes_to_keep_under_moderate_pressure_); |
177 } | 209 } |
178 | 210 |
179 void DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin( | 211 bool DiscardableMemoryManager:: |
180 size_t limit) { | 212 PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { |
181 TRACE_EVENT1( | 213 AutoLock lock(lock_); |
182 "base", | |
183 "DiscardableMemoryManager::PurgeLRUWithLockAcquiredUntilUsageIsWithin", | |
184 "limit", | |
185 limit); | |
186 | 214 |
| 215 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 216 Now() - hard_memory_limit_expiration_time_, soft_memory_limit_); |
| 217 |
| 218 return bytes_allocated_ <= soft_memory_limit_; |
| 219 } |
| 220 |
| 221 void DiscardableMemoryManager:: |
| 222 PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( |
| 223 TimeTicks timestamp, |
| 224 size_t limit) { |
187 lock_.AssertAcquired(); | 225 lock_.AssertAcquired(); |
188 | 226 |
189 size_t bytes_allocated_before_purging = bytes_allocated_; | 227 size_t bytes_allocated_before_purging = bytes_allocated_; |
190 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); | 228 for (AllocationMap::reverse_iterator it = allocations_.rbegin(); |
191 it != allocations_.rend(); | 229 it != allocations_.rend(); |
192 ++it) { | 230 ++it) { |
193 Allocation* allocation = it->first; | 231 Allocation* allocation = it->first; |
194 AllocationInfo* info = &it->second; | 232 AllocationInfo* info = &it->second; |
195 | 233 |
196 if (bytes_allocated_ <= limit) | 234 if (bytes_allocated_ <= limit) |
197 break; | 235 break; |
198 if (!info->purgable) | 236 |
| 237 bool purgable = info->purgable && info->last_usage <= timestamp; |
| 238 if (!purgable) |
199 continue; | 239 continue; |
200 | 240 |
201 size_t bytes_purgable = info->bytes; | 241 size_t bytes_purgable = info->bytes; |
202 DCHECK_LE(bytes_purgable, bytes_allocated_); | 242 DCHECK_LE(bytes_purgable, bytes_allocated_); |
203 bytes_allocated_ -= bytes_purgable; | 243 bytes_allocated_ -= bytes_purgable; |
204 info->purgable = false; | 244 info->purgable = false; |
205 allocation->Purge(); | 245 allocation->Purge(); |
206 } | 246 } |
207 | 247 |
208 if (bytes_allocated_ != bytes_allocated_before_purging) | 248 if (bytes_allocated_ != bytes_allocated_before_purging) |
209 BytesAllocatedChanged(); | 249 BytesAllocatedChanged(bytes_allocated_); |
210 } | 250 } |
211 | 251 |
212 void DiscardableMemoryManager::EnforcePolicyWithLockAcquired() { | 252 void DiscardableMemoryManager::BytesAllocatedChanged( |
213 PurgeLRUWithLockAcquiredUntilUsageIsWithin(memory_limit_); | 253 size_t new_bytes_allocated) const { |
214 } | 254 TRACE_COUNTER_ID1( |
215 | 255 "base", "DiscardableMemoryUsage", this, new_bytes_allocated); |
216 void DiscardableMemoryManager::BytesAllocatedChanged() const { | |
217 TRACE_COUNTER_ID1("base", "DiscardableMemoryUsage", this, bytes_allocated_); | |
218 | 256 |
219 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; | 257 static const char kDiscardableMemoryUsageKey[] = "dm-usage"; |
220 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, | 258 base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, |
221 Uint64ToString(bytes_allocated_)); | 259 Uint64ToString(new_bytes_allocated)); |
222 } | 260 } |
223 | 261 |
224 } // namespace internal | 262 } // namespace internal |
225 } // namespace base | 263 } // namespace base |
OLD | NEW |