Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(580)

Side by Side Diff: base/memory/discardable_memory_manager.cc

Issue 336273003: base: Add soft memory limit to DiscardableMemoryManager. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix mac build Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW
« no previous file with comments | « base/memory/discardable_memory_manager.h ('k') | base/memory/discardable_memory_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698