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