OLD | NEW |
1 /* | 1 /* |
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) |
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) |
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) |
5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | 5 Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. |
6 | 6 |
7 This library is free software; you can redistribute it and/or | 7 This library is free software; you can redistribute it and/or |
8 modify it under the terms of the GNU Library General Public | 8 modify it under the terms of the GNU Library General Public |
9 License as published by the Free Software Foundation; either | 9 License as published by the Free Software Foundation; either |
10 version 2 of the License, or (at your option) any later version. | 10 version 2 of the License, or (at your option) any later version. |
(...skipping 20 matching lines...) Expand all Loading... |
31 #include "wtf/AutoReset.h" | 31 #include "wtf/AutoReset.h" |
32 #include "wtf/CurrentTime.h" | 32 #include "wtf/CurrentTime.h" |
33 #include "wtf/MathExtras.h" | 33 #include "wtf/MathExtras.h" |
34 #include "wtf/text/CString.h" | 34 #include "wtf/text/CString.h" |
35 | 35 |
36 namespace blink { | 36 namespace blink { |
37 | 37 |
38 static Persistent<MemoryCache>* gMemoryCache; | 38 static Persistent<MemoryCache>* gMemoryCache; |
39 | 39 |
40 static const unsigned cDefaultCacheCapacity = 8192 * 1024; | 40 static const unsigned cDefaultCacheCapacity = 8192 * 1024; |
41 static const unsigned cDeferredPruneDeadCapacityFactor = 2; | |
42 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. | 41 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. |
43 static const double cMaxPruneDeferralDelay = 0.5; // Seconds. | 42 static const double cMaxPruneDeferralDelay = 0.5; // Seconds. |
44 | 43 |
45 // Percentage of capacity toward which we prune, to avoid immediately pruning | 44 // Percentage of capacity toward which we prune, to avoid immediately pruning |
46 // again. | 45 // again. |
47 static const float cTargetPrunePercentage = .95f; | 46 static const float cTargetPrunePercentage = .95f; |
48 | 47 |
49 MemoryCache* memoryCache() { | 48 MemoryCache* memoryCache() { |
50 DCHECK(WTF::isMainThread()); | 49 DCHECK(WTF::isMainThread()); |
51 if (!gMemoryCache) | 50 if (!gMemoryCache) |
52 gMemoryCache = new Persistent<MemoryCache>(MemoryCache::create()); | 51 gMemoryCache = new Persistent<MemoryCache>(MemoryCache::create()); |
53 return gMemoryCache->get(); | 52 return gMemoryCache->get(); |
54 } | 53 } |
55 | 54 |
56 MemoryCache* replaceMemoryCacheForTesting(MemoryCache* cache) { | 55 MemoryCache* replaceMemoryCacheForTesting(MemoryCache* cache) { |
57 memoryCache(); | 56 memoryCache(); |
58 MemoryCache* oldCache = gMemoryCache->release(); | 57 MemoryCache* oldCache = gMemoryCache->release(); |
59 *gMemoryCache = cache; | 58 *gMemoryCache = cache; |
60 MemoryCacheDumpProvider::instance()->setMemoryCache(cache); | 59 MemoryCacheDumpProvider::instance()->setMemoryCache(cache); |
61 return oldCache; | 60 return oldCache; |
62 } | 61 } |
63 | 62 |
64 DEFINE_TRACE(MemoryCacheEntry) { | 63 DEFINE_TRACE(MemoryCacheEntry) { |
65 visitor->template registerWeakMembers<MemoryCacheEntry, | 64 visitor->template registerWeakMembers<MemoryCacheEntry, |
66 &MemoryCacheEntry::clearResourceWeak>( | 65 &MemoryCacheEntry::clearResourceWeak>( |
67 this); | 66 this); |
68 visitor->trace(m_previousInLiveResourcesList); | |
69 visitor->trace(m_nextInLiveResourcesList); | |
70 visitor->trace(m_previousInAllResourcesList); | |
71 visitor->trace(m_nextInAllResourcesList); | |
72 } | 67 } |
73 | 68 |
74 void MemoryCacheEntry::clearResourceWeak(Visitor* visitor) { | 69 void MemoryCacheEntry::clearResourceWeak(Visitor* visitor) { |
75 if (!m_resource || ThreadHeap::isHeapObjectAlive(m_resource)) | 70 if (!m_resource || ThreadHeap::isHeapObjectAlive(m_resource)) |
76 return; | 71 return; |
77 memoryCache()->remove(m_resource.get()); | 72 memoryCache()->remove(m_resource.get()); |
78 m_resource.clear(); | 73 m_resource.clear(); |
79 } | 74 } |
80 | 75 |
81 void MemoryCacheEntry::dispose() { | |
82 m_resource.clear(); | |
83 } | |
84 | |
85 Resource* MemoryCacheEntry::resource() { | |
86 return m_resource.get(); | |
87 } | |
88 | |
89 DEFINE_TRACE(MemoryCacheLRUList) { | |
90 visitor->trace(m_head); | |
91 visitor->trace(m_tail); | |
92 } | |
93 | |
94 inline MemoryCache::MemoryCache() | 76 inline MemoryCache::MemoryCache() |
95 : m_inPruneResources(false), | 77 : m_inPruneResources(false), |
96 m_prunePending(false), | 78 m_prunePending(false), |
97 m_maxPruneDeferralDelay(cMaxPruneDeferralDelay), | 79 m_maxPruneDeferralDelay(cMaxPruneDeferralDelay), |
98 m_pruneTimeStamp(0.0), | 80 m_pruneTimeStamp(0.0), |
99 m_pruneFrameTimeStamp(0.0), | 81 m_pruneFrameTimeStamp(0.0), |
100 m_lastFramePaintTimeStamp(0.0), | 82 m_lastFramePaintTimeStamp(0.0), |
101 m_capacity(cDefaultCacheCapacity), | 83 m_capacity(cDefaultCacheCapacity), |
102 m_minDeadCapacity(0), | |
103 m_maxDeadCapacity(cDefaultCacheCapacity), | |
104 m_maxDeferredPruneDeadCapacity(cDeferredPruneDeadCapacityFactor * | |
105 cDefaultCacheCapacity), | |
106 m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune), | 84 m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune), |
107 m_liveSize(0), | 85 m_size(0) { |
108 m_deadSize(0) { | |
109 MemoryCacheDumpProvider::instance()->setMemoryCache(this); | 86 MemoryCacheDumpProvider::instance()->setMemoryCache(this); |
110 if (ProcessHeap::isLowEndDevice()) | 87 if (ProcessHeap::isLowEndDevice()) |
111 MemoryCoordinator::instance().registerClient(this); | 88 MemoryCoordinator::instance().registerClient(this); |
112 } | 89 } |
113 | 90 |
114 MemoryCache* MemoryCache::create() { | 91 MemoryCache* MemoryCache::create() { |
115 return new MemoryCache; | 92 return new MemoryCache; |
116 } | 93 } |
117 | 94 |
118 MemoryCache::~MemoryCache() { | 95 MemoryCache::~MemoryCache() { |
119 if (m_prunePending) | 96 if (m_prunePending) |
120 Platform::current()->currentThread()->removeTaskObserver(this); | 97 Platform::current()->currentThread()->removeTaskObserver(this); |
121 } | 98 } |
122 | 99 |
123 DEFINE_TRACE(MemoryCache) { | 100 DEFINE_TRACE(MemoryCache) { |
124 visitor->trace(m_allResources); | |
125 visitor->trace(m_liveDecodedResources); | |
126 visitor->trace(m_resourceMaps); | 101 visitor->trace(m_resourceMaps); |
127 MemoryCacheDumpClient::trace(visitor); | 102 MemoryCacheDumpClient::trace(visitor); |
128 MemoryCoordinatorClient::trace(visitor); | 103 MemoryCoordinatorClient::trace(visitor); |
129 } | 104 } |
130 | 105 |
131 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) { | 106 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) { |
132 if (!originalURL.hasFragmentIdentifier()) | 107 if (!originalURL.hasFragmentIdentifier()) |
133 return originalURL; | 108 return originalURL; |
134 // Strip away fragment identifier from HTTP URLs. Data URLs must be | 109 // Strip away fragment identifier from HTTP URLs. Data URLs must be |
135 // unmodified. For file and custom URLs clients may expect resources to be | 110 // unmodified. For file and custom URLs clients may expect resources to be |
(...skipping 13 matching lines...) Expand all Loading... |
149 const String& cacheIdentifier) { | 124 const String& cacheIdentifier) { |
150 if (!m_resourceMaps.contains(cacheIdentifier)) { | 125 if (!m_resourceMaps.contains(cacheIdentifier)) { |
151 ResourceMapIndex::AddResult result = | 126 ResourceMapIndex::AddResult result = |
152 m_resourceMaps.add(cacheIdentifier, new ResourceMap); | 127 m_resourceMaps.add(cacheIdentifier, new ResourceMap); |
153 CHECK(result.isNewEntry); | 128 CHECK(result.isNewEntry); |
154 } | 129 } |
155 return m_resourceMaps.get(cacheIdentifier); | 130 return m_resourceMaps.get(cacheIdentifier); |
156 } | 131 } |
157 | 132 |
158 void MemoryCache::add(Resource* resource) { | 133 void MemoryCache::add(Resource* resource) { |
159 DCHECK(WTF::isMainThread()); | 134 DCHECK(resource); |
160 DCHECK(resource->url().isValid()); | |
161 ResourceMap* resources = ensureResourceMap(resource->cacheIdentifier()); | 135 ResourceMap* resources = ensureResourceMap(resource->cacheIdentifier()); |
162 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); | 136 addInternal(resources, MemoryCacheEntry::create(resource)); |
163 CHECK(!contains(resource)); | |
164 resources->set(url, MemoryCacheEntry::create(resource)); | |
165 update(resource, 0, resource->size(), true); | |
166 | |
167 RESOURCE_LOADING_DVLOG(1) << "MemoryCache::add Added " | 137 RESOURCE_LOADING_DVLOG(1) << "MemoryCache::add Added " |
168 << resource->url().getString() << ", resource " | 138 << resource->url().getString() << ", resource " |
169 << resource; | 139 << resource; |
170 } | 140 } |
171 | 141 |
| 142 void MemoryCache::addInternal(ResourceMap* resourceMap, |
| 143 MemoryCacheEntry* entry) { |
| 144 DCHECK(WTF::isMainThread()); |
| 145 DCHECK(resourceMap); |
| 146 |
| 147 Resource* resource = entry->resource(); |
| 148 if (!resource) |
| 149 return; |
| 150 DCHECK(resource->url().isValid()); |
| 151 |
| 152 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); |
| 153 ResourceMap::iterator it = resourceMap->find(url); |
| 154 if (it != resourceMap->end()) { |
| 155 Resource* oldResource = it->value->resource(); |
| 156 CHECK_NE(oldResource, resource); |
| 157 update(oldResource, oldResource->size(), 0); |
| 158 } |
| 159 resourceMap->set(url, entry); |
| 160 update(resource, 0, resource->size()); |
| 161 } |
| 162 |
172 void MemoryCache::remove(Resource* resource) { | 163 void MemoryCache::remove(Resource* resource) { |
173 // The resource may have already been removed by someone other than our | 164 DCHECK(WTF::isMainThread()); |
174 // caller, who needed a fresh copy for a reload. | 165 DCHECK(resource); |
175 if (MemoryCacheEntry* entry = getEntryForResource(resource)) | 166 RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for " |
176 evict(entry); | 167 << resource->url().getString() << " from cache"; |
| 168 TRACE_EVENT1("blink", "MemoryCache::evict", "resource", |
| 169 resource->url().getString().utf8()); |
| 170 |
| 171 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier()); |
| 172 if (!resources) |
| 173 return; |
| 174 |
| 175 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); |
| 176 ResourceMap::iterator it = resources->find(url); |
| 177 if (it == resources->end() || it->value->resource() != resource) |
| 178 return; |
| 179 removeInternal(resources, it); |
| 180 } |
| 181 |
| 182 void MemoryCache::removeInternal(ResourceMap* resourceMap, |
| 183 const ResourceMap::iterator& it) { |
| 184 DCHECK(WTF::isMainThread()); |
| 185 DCHECK(resourceMap); |
| 186 |
| 187 Resource* resource = it->value->resource(); |
| 188 DCHECK(resource); |
| 189 |
| 190 update(resource, resource->size(), 0); |
| 191 resourceMap->remove(it); |
177 } | 192 } |
178 | 193 |
179 bool MemoryCache::contains(const Resource* resource) const { | 194 bool MemoryCache::contains(const Resource* resource) const { |
180 return getEntryForResource(resource); | 195 if (!resource || resource->url().isEmpty()) |
| 196 return false; |
| 197 const ResourceMap* resources = |
| 198 m_resourceMaps.get(resource->cacheIdentifier()); |
| 199 if (!resources) |
| 200 return false; |
| 201 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); |
| 202 MemoryCacheEntry* entry = resources->get(url); |
| 203 return entry && resource == entry->resource(); |
181 } | 204 } |
182 | 205 |
183 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) { | 206 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) const { |
184 return resourceForURL(resourceURL, defaultCacheIdentifier()); | 207 return resourceForURL(resourceURL, defaultCacheIdentifier()); |
185 } | 208 } |
186 | 209 |
187 Resource* MemoryCache::resourceForURL(const KURL& resourceURL, | 210 Resource* MemoryCache::resourceForURL(const KURL& resourceURL, |
188 const String& cacheIdentifier) { | 211 const String& cacheIdentifier) const { |
189 DCHECK(WTF::isMainThread()); | 212 DCHECK(WTF::isMainThread()); |
190 if (!resourceURL.isValid() || resourceURL.isNull()) | 213 if (!resourceURL.isValid() || resourceURL.isNull()) |
191 return nullptr; | 214 return nullptr; |
192 DCHECK(!cacheIdentifier.isNull()); | 215 DCHECK(!cacheIdentifier.isNull()); |
193 ResourceMap* resources = m_resourceMaps.get(cacheIdentifier); | 216 const ResourceMap* resources = m_resourceMaps.get(cacheIdentifier); |
194 if (!resources) | 217 if (!resources) |
195 return nullptr; | 218 return nullptr; |
196 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); | 219 MemoryCacheEntry* entry = |
197 MemoryCacheEntry* entry = resources->get(url); | 220 resources->get(removeFragmentIdentifierIfNeeded(resourceURL)); |
198 if (!entry) | 221 if (!entry) |
199 return nullptr; | 222 return nullptr; |
200 return entry->resource(); | 223 return entry->resource(); |
201 } | 224 } |
202 | 225 |
203 HeapVector<Member<Resource>> MemoryCache::resourcesForURL( | 226 HeapVector<Member<Resource>> MemoryCache::resourcesForURL( |
204 const KURL& resourceURL) { | 227 const KURL& resourceURL) const { |
205 DCHECK(WTF::isMainThread()); | 228 DCHECK(WTF::isMainThread()); |
206 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); | 229 KURL url = removeFragmentIdentifierIfNeeded(resourceURL); |
207 HeapVector<Member<Resource>> results; | 230 HeapVector<Member<Resource>> results; |
208 for (const auto& resourceMapIter : m_resourceMaps) { | 231 for (const auto& resourceMapIter : m_resourceMaps) { |
209 if (MemoryCacheEntry* entry = resourceMapIter.value->get(url)) { | 232 if (MemoryCacheEntry* entry = resourceMapIter.value->get(url)) { |
210 Resource* resource = entry->resource(); | 233 Resource* resource = entry->resource(); |
211 DCHECK(resource); | 234 DCHECK(resource); |
212 results.append(resource); | 235 results.append(resource); |
213 } | 236 } |
214 } | 237 } |
215 return results; | 238 return results; |
216 } | 239 } |
217 | 240 |
218 size_t MemoryCache::deadCapacity() const { | 241 void MemoryCache::pruneResources(PruneStrategy strategy) { |
219 // Dead resource capacity is whatever space is not occupied by live resources, | |
220 // bounded by an independent minimum and maximum. | |
221 size_t capacity = | |
222 m_capacity - | |
223 std::min(m_liveSize, m_capacity); // Start with available capacity. | |
224 capacity = std::max(capacity, | |
225 m_minDeadCapacity); // Make sure it's above the minimum. | |
226 capacity = std::min(capacity, | |
227 m_maxDeadCapacity); // Make sure it's below the maximum. | |
228 return capacity; | |
229 } | |
230 | |
231 size_t MemoryCache::liveCapacity() const { | |
232 // Live resource capacity is whatever is left over after calculating dead | |
233 // resource capacity. | |
234 return m_capacity - deadCapacity(); | |
235 } | |
236 | |
237 void MemoryCache::pruneLiveResources(PruneStrategy strategy) { | |
238 DCHECK(!m_prunePending); | 242 DCHECK(!m_prunePending); |
239 size_t capacity = liveCapacity(); | 243 const size_t sizeLimit = (strategy == MaximalPrune) ? 0 : capacity(); |
240 if (strategy == MaximalPrune) | 244 if (m_size <= sizeLimit) |
241 capacity = 0; | |
242 if (!m_liveSize || (capacity && m_liveSize <= capacity)) | |
243 return; | 245 return; |
244 | 246 |
245 // Cut by a percentage to avoid immediately pruning again. | 247 // Cut by a percentage to avoid immediately pruning again. |
246 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage); | 248 size_t targetSize = static_cast<size_t>(sizeLimit * cTargetPrunePercentage); |
247 | 249 |
248 // Destroy any decoded data in live objects that we can. Start from the tail, | 250 for (const auto& resourceMapIter : m_resourceMaps) { |
249 // since this is the lowest priority and least recently accessed of the | 251 for (const auto& resourceIter : *resourceMapIter.value) { |
250 // objects. | 252 Resource* resource = resourceIter.value->resource(); |
251 | 253 DCHECK(resource); |
252 // The list might not be sorted by the m_lastDecodedFrameTimeStamp. The impact | 254 if (resource->isLoaded() && resource->decodedSize()) { |
253 // of this weaker invariant is minor as the below if statement to check the | 255 // Check to see if the remaining resources are too new to prune. |
254 // elapsedTime will evaluate to false as the current time will be a lot | 256 double elapsedTime = |
255 // greater than the current->m_lastDecodedFrameTimeStamp. For more details | 257 m_pruneFrameTimeStamp - resourceIter.value->m_lastDecodedAccessTime; |
256 // see: https://bugs.webkit.org/show_bug.cgi?id=30209 | 258 if (strategy == AutomaticPrune && |
257 | 259 elapsedTime < m_delayBeforeLiveDecodedPrune) |
258 MemoryCacheEntry* current = m_liveDecodedResources.m_tail; | 260 continue; |
259 while (current) { | 261 resource->prune(); |
260 Resource* resource = current->resource(); | 262 if (m_size <= targetSize) |
261 MemoryCacheEntry* previous = current->m_previousInLiveResourcesList; | 263 return; |
262 DCHECK(resource->isAlive()); | 264 } |
263 | |
264 if (resource->isLoaded() && resource->decodedSize()) { | |
265 // Check to see if the remaining resources are too new to prune. | |
266 double elapsedTime = | |
267 m_pruneFrameTimeStamp - current->m_lastDecodedAccessTime; | |
268 if (strategy == AutomaticPrune && | |
269 elapsedTime < m_delayBeforeLiveDecodedPrune) | |
270 return; | |
271 | |
272 // Destroy our decoded data if possible. This will remove us from | |
273 // m_liveDecodedResources, and possibly move us to a different LRU list in | |
274 // m_allResources. | |
275 resource->prune(); | |
276 | |
277 if (targetSize && m_liveSize <= targetSize) | |
278 return; | |
279 } | 265 } |
280 current = previous; | |
281 } | 266 } |
282 } | 267 } |
283 | 268 |
284 void MemoryCache::pruneDeadResources(PruneStrategy strategy) { | 269 void MemoryCache::setCapacity(size_t totalBytes) { |
285 size_t capacity = deadCapacity(); | |
286 if (strategy == MaximalPrune) | |
287 capacity = 0; | |
288 if (!m_deadSize || (capacity && m_deadSize <= capacity)) | |
289 return; | |
290 | |
291 // Cut by a percentage to avoid immediately pruning again. | |
292 size_t targetSize = static_cast<size_t>(capacity * cTargetPrunePercentage); | |
293 | |
294 int size = m_allResources.size(); | |
295 if (targetSize && m_deadSize <= targetSize) | |
296 return; | |
297 | |
298 bool canShrinkLRULists = true; | |
299 for (int i = size - 1; i >= 0; i--) { | |
300 // Remove from the tail, since this is the least frequently accessed of the | |
301 // objects. | |
302 MemoryCacheEntry* current = m_allResources[i].m_tail; | |
303 | |
304 // First flush all the decoded data in this queue. | |
305 while (current) { | |
306 Resource* resource = current->resource(); | |
307 MemoryCacheEntry* previous = current->m_previousInAllResourcesList; | |
308 | |
309 // Decoded data may reference other resources. Skip |current| if |current| | |
310 // somehow got kicked out of cache during destroyDecodedData(). | |
311 if (!resource || !contains(resource)) { | |
312 current = previous; | |
313 continue; | |
314 } | |
315 | |
316 if (!resource->isAlive() && !resource->isPreloaded() && | |
317 resource->isLoaded()) { | |
318 // Destroy our decoded data. This will remove us from | |
319 // m_liveDecodedResources, and possibly move us to a different LRU list | |
320 // in m_allResources. | |
321 resource->prune(); | |
322 | |
323 if (targetSize && m_deadSize <= targetSize) | |
324 return; | |
325 } | |
326 current = previous; | |
327 } | |
328 | |
329 // Now evict objects from this queue. | |
330 current = m_allResources[i].m_tail; | |
331 while (current) { | |
332 Resource* resource = current->resource(); | |
333 MemoryCacheEntry* previous = current->m_previousInAllResourcesList; | |
334 if (!resource || !contains(resource)) { | |
335 current = previous; | |
336 continue; | |
337 } | |
338 if (!resource->isAlive() && !resource->isPreloaded()) { | |
339 evict(current); | |
340 if (targetSize && m_deadSize <= targetSize) | |
341 return; | |
342 } | |
343 current = previous; | |
344 } | |
345 | |
346 // Shrink the vector back down so we don't waste time inspecting empty LRU | |
347 // lists on future prunes. | |
348 if (m_allResources[i].m_head) | |
349 canShrinkLRULists = false; | |
350 else if (canShrinkLRULists) | |
351 m_allResources.resize(i); | |
352 } | |
353 } | |
354 | |
355 void MemoryCache::setCapacities(size_t minDeadBytes, | |
356 size_t maxDeadBytes, | |
357 size_t totalBytes) { | |
358 DCHECK_LE(minDeadBytes, maxDeadBytes); | |
359 DCHECK_LE(maxDeadBytes, totalBytes); | |
360 m_minDeadCapacity = minDeadBytes; | |
361 m_maxDeadCapacity = maxDeadBytes; | |
362 m_maxDeferredPruneDeadCapacity = | |
363 cDeferredPruneDeadCapacityFactor * maxDeadBytes; | |
364 m_capacity = totalBytes; | 270 m_capacity = totalBytes; |
365 prune(); | 271 prune(); |
366 } | 272 } |
367 | 273 |
368 void MemoryCache::evict(MemoryCacheEntry* entry) { | 274 void MemoryCache::update(Resource* resource, size_t oldSize, size_t newSize) { |
369 DCHECK(WTF::isMainThread()); | |
370 | |
371 Resource* resource = entry->resource(); | |
372 DCHECK(resource); | |
373 RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for " | |
374 << resource->url().getString() << " from cache"; | |
375 TRACE_EVENT1("blink", "MemoryCache::evict", "resource", | |
376 resource->url().getString().utf8()); | |
377 // The resource may have already been removed by someone other than our | |
378 // caller, who needed a fresh copy for a reload. See | |
379 // <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. | |
380 update(resource, resource->size(), 0, false); | |
381 removeFromLiveDecodedResourcesList(entry); | |
382 | |
383 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier()); | |
384 DCHECK(resources); | |
385 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); | |
386 ResourceMap::iterator it = resources->find(url); | |
387 DCHECK_NE(it, resources->end()); | |
388 | |
389 MemoryCacheEntry* entryPtr = it->value; | |
390 resources->remove(it); | |
391 if (entryPtr) | |
392 entryPtr->dispose(); | |
393 } | |
394 | |
395 MemoryCacheEntry* MemoryCache::getEntryForResource( | |
396 const Resource* resource) const { | |
397 if (!resource || resource->url().isNull() || resource->url().isEmpty()) | |
398 return nullptr; | |
399 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier()); | |
400 if (!resources) | |
401 return nullptr; | |
402 KURL url = removeFragmentIdentifierIfNeeded(resource->url()); | |
403 MemoryCacheEntry* entry = resources->get(url); | |
404 if (!entry || entry->resource() != resource) | |
405 return nullptr; | |
406 return entry; | |
407 } | |
408 | |
409 MemoryCacheLRUList* MemoryCache::lruListFor(unsigned accessCount, size_t size) { | |
410 DCHECK_GT(accessCount, 0u); | |
411 unsigned queueIndex = WTF::fastLog2(size / accessCount); | |
412 if (m_allResources.size() <= queueIndex) | |
413 m_allResources.grow(queueIndex + 1); | |
414 return &m_allResources[queueIndex]; | |
415 } | |
416 | |
417 void MemoryCache::removeFromLRUList(MemoryCacheEntry* entry, | |
418 MemoryCacheLRUList* list) { | |
419 DCHECK(containedInLRUList(entry, list)); | |
420 | |
421 MemoryCacheEntry* next = entry->m_nextInAllResourcesList; | |
422 MemoryCacheEntry* previous = entry->m_previousInAllResourcesList; | |
423 entry->m_nextInAllResourcesList = nullptr; | |
424 entry->m_previousInAllResourcesList = nullptr; | |
425 | |
426 if (next) | |
427 next->m_previousInAllResourcesList = previous; | |
428 else | |
429 list->m_tail = previous; | |
430 | |
431 if (previous) | |
432 previous->m_nextInAllResourcesList = next; | |
433 else | |
434 list->m_head = next; | |
435 | |
436 DCHECK(!containedInLRUList(entry, list)); | |
437 } | |
438 | |
439 void MemoryCache::insertInLRUList(MemoryCacheEntry* entry, | |
440 MemoryCacheLRUList* list) { | |
441 DCHECK(!containedInLRUList(entry, list)); | |
442 | |
443 entry->m_nextInAllResourcesList = list->m_head; | |
444 list->m_head = entry; | |
445 | |
446 if (entry->m_nextInAllResourcesList) | |
447 entry->m_nextInAllResourcesList->m_previousInAllResourcesList = entry; | |
448 else | |
449 list->m_tail = entry; | |
450 | |
451 DCHECK(containedInLRUList(entry, list)); | |
452 } | |
453 | |
454 bool MemoryCache::containedInLRUList(MemoryCacheEntry* entry, | |
455 MemoryCacheLRUList* list) { | |
456 for (MemoryCacheEntry* current = list->m_head; current; | |
457 current = current->m_nextInAllResourcesList) { | |
458 if (current == entry) | |
459 return true; | |
460 } | |
461 DCHECK(!entry->m_nextInAllResourcesList); | |
462 DCHECK(!entry->m_previousInAllResourcesList); | |
463 return false; | |
464 } | |
465 | |
466 void MemoryCache::removeFromLiveDecodedResourcesList(MemoryCacheEntry* entry) { | |
467 // If we've never been accessed, then we're brand new and not in any list. | |
468 if (!entry->m_inLiveDecodedResourcesList) | |
469 return; | |
470 DCHECK(containedInLiveDecodedResourcesList(entry)); | |
471 | |
472 entry->m_inLiveDecodedResourcesList = false; | |
473 | |
474 MemoryCacheEntry* next = entry->m_nextInLiveResourcesList; | |
475 MemoryCacheEntry* previous = entry->m_previousInLiveResourcesList; | |
476 | |
477 entry->m_nextInLiveResourcesList = nullptr; | |
478 entry->m_previousInLiveResourcesList = nullptr; | |
479 | |
480 if (next) | |
481 next->m_previousInLiveResourcesList = previous; | |
482 else | |
483 m_liveDecodedResources.m_tail = previous; | |
484 | |
485 if (previous) | |
486 previous->m_nextInLiveResourcesList = next; | |
487 else | |
488 m_liveDecodedResources.m_head = next; | |
489 | |
490 DCHECK(!containedInLiveDecodedResourcesList(entry)); | |
491 } | |
492 | |
493 void MemoryCache::insertInLiveDecodedResourcesList(MemoryCacheEntry* entry) { | |
494 DCHECK(!containedInLiveDecodedResourcesList(entry)); | |
495 | |
496 entry->m_inLiveDecodedResourcesList = true; | |
497 | |
498 entry->m_nextInLiveResourcesList = m_liveDecodedResources.m_head; | |
499 if (m_liveDecodedResources.m_head) | |
500 m_liveDecodedResources.m_head->m_previousInLiveResourcesList = entry; | |
501 m_liveDecodedResources.m_head = entry; | |
502 | |
503 if (!entry->m_nextInLiveResourcesList) | |
504 m_liveDecodedResources.m_tail = entry; | |
505 | |
506 DCHECK(containedInLiveDecodedResourcesList(entry)); | |
507 } | |
508 | |
509 bool MemoryCache::containedInLiveDecodedResourcesList(MemoryCacheEntry* entry) { | |
510 for (MemoryCacheEntry* current = m_liveDecodedResources.m_head; current; | |
511 current = current->m_nextInLiveResourcesList) { | |
512 if (current == entry) { | |
513 DCHECK(entry->m_inLiveDecodedResourcesList); | |
514 return true; | |
515 } | |
516 } | |
517 DCHECK(!entry->m_nextInLiveResourcesList); | |
518 DCHECK(!entry->m_previousInLiveResourcesList); | |
519 DCHECK(!entry->m_inLiveDecodedResourcesList); | |
520 return false; | |
521 } | |
522 | |
523 void MemoryCache::makeLive(Resource* resource) { | |
524 if (!contains(resource)) | 275 if (!contains(resource)) |
525 return; | 276 return; |
526 DCHECK_GE(m_deadSize, resource->size()); | |
527 m_liveSize += resource->size(); | |
528 m_deadSize -= resource->size(); | |
529 } | |
530 | |
531 void MemoryCache::makeDead(Resource* resource) { | |
532 if (!contains(resource)) | |
533 return; | |
534 m_liveSize -= resource->size(); | |
535 m_deadSize += resource->size(); | |
536 removeFromLiveDecodedResourcesList(getEntryForResource(resource)); | |
537 } | |
538 | |
539 void MemoryCache::update(Resource* resource, | |
540 size_t oldSize, | |
541 size_t newSize, | |
542 bool wasAccessed) { | |
543 MemoryCacheEntry* entry = getEntryForResource(resource); | |
544 if (!entry) | |
545 return; | |
546 | |
547 // The object must now be moved to a different queue, since either its size or | |
548 // its accessCount has been changed, and both of those are used to determine | |
549 // which LRU queue the resource should be in. | |
550 if (oldSize) | |
551 removeFromLRUList(entry, lruListFor(entry->m_accessCount, oldSize)); | |
552 if (wasAccessed) | |
553 entry->m_accessCount++; | |
554 if (newSize) | |
555 insertInLRUList(entry, lruListFor(entry->m_accessCount, newSize)); | |
556 | |
557 ptrdiff_t delta = newSize - oldSize; | 277 ptrdiff_t delta = newSize - oldSize; |
558 if (resource->isAlive()) { | 278 DCHECK(delta >= 0 || m_size >= static_cast<size_t>(-delta)); |
559 DCHECK(delta >= 0 || m_liveSize >= static_cast<size_t>(-delta)); | 279 m_size += delta; |
560 m_liveSize += delta; | |
561 } else { | |
562 DCHECK(delta >= 0 || m_deadSize >= static_cast<size_t>(-delta)); | |
563 m_deadSize += delta; | |
564 } | |
565 } | |
566 | |
567 void MemoryCache::updateDecodedResource(Resource* resource, | |
568 UpdateReason reason) { | |
569 MemoryCacheEntry* entry = getEntryForResource(resource); | |
570 if (!entry) | |
571 return; | |
572 | |
573 removeFromLiveDecodedResourcesList(entry); | |
574 if (resource->decodedSize() && resource->isAlive()) | |
575 insertInLiveDecodedResourcesList(entry); | |
576 | |
577 if (reason != UpdateForAccess) | |
578 return; | |
579 | |
580 double timestamp = resource->isImage() ? m_lastFramePaintTimeStamp : 0.0; | |
581 if (!timestamp) | |
582 timestamp = currentTime(); | |
583 entry->m_lastDecodedAccessTime = timestamp; | |
584 } | 280 } |
585 | 281 |
586 void MemoryCache::removeURLFromCache(const KURL& url) { | 282 void MemoryCache::removeURLFromCache(const KURL& url) { |
587 HeapVector<Member<Resource>> resources = resourcesForURL(url); | 283 HeapVector<Member<Resource>> resources = resourcesForURL(url); |
588 for (Resource* resource : resources) | 284 for (Resource* resource : resources) |
589 memoryCache()->remove(resource); | 285 remove(resource); |
590 } | 286 } |
591 | 287 |
592 void MemoryCache::TypeStatistic::addResource(Resource* o) { | 288 void MemoryCache::TypeStatistic::addResource(Resource* o) { |
593 count++; | 289 count++; |
594 size += o->size(); | 290 size += o->size(); |
595 liveSize += o->isAlive() ? o->size() : 0; | |
596 decodedSize += o->decodedSize(); | 291 decodedSize += o->decodedSize(); |
597 encodedSize += o->encodedSize(); | 292 encodedSize += o->encodedSize(); |
598 overheadSize += o->overheadSize(); | 293 overheadSize += o->overheadSize(); |
599 encodedSizeDuplicatedInDataURLs += | 294 encodedSizeDuplicatedInDataURLs += |
600 o->url().protocolIsData() ? o->encodedSize() : 0; | 295 o->url().protocolIsData() ? o->encodedSize() : 0; |
601 } | 296 } |
602 | 297 |
603 MemoryCache::Statistics MemoryCache::getStatistics() { | 298 MemoryCache::Statistics MemoryCache::getStatistics() const { |
604 Statistics stats; | 299 Statistics stats; |
605 for (const auto& resourceMapIter : m_resourceMaps) { | 300 for (const auto& resourceMapIter : m_resourceMaps) { |
606 for (const auto& resourceIter : *resourceMapIter.value) { | 301 for (const auto& resourceIter : *resourceMapIter.value) { |
607 Resource* resource = resourceIter.value->resource(); | 302 Resource* resource = resourceIter.value->resource(); |
608 DCHECK(resource); | 303 DCHECK(resource); |
609 switch (resource->getType()) { | 304 switch (resource->getType()) { |
610 case Resource::Image: | 305 case Resource::Image: |
611 stats.images.addResource(resource); | 306 stats.images.addResource(resource); |
612 break; | 307 break; |
613 case Resource::CSSStyleSheet: | 308 case Resource::CSSStyleSheet: |
(...skipping 20 matching lines...) Expand all Loading... |
634 void MemoryCache::evictResources(EvictResourcePolicy policy) { | 329 void MemoryCache::evictResources(EvictResourcePolicy policy) { |
635 for (auto resourceMapIter = m_resourceMaps.begin(); | 330 for (auto resourceMapIter = m_resourceMaps.begin(); |
636 resourceMapIter != m_resourceMaps.end();) { | 331 resourceMapIter != m_resourceMaps.end();) { |
637 ResourceMap* resources = resourceMapIter->value.get(); | 332 ResourceMap* resources = resourceMapIter->value.get(); |
638 HeapVector<Member<MemoryCacheEntry>> unusedPreloads; | 333 HeapVector<Member<MemoryCacheEntry>> unusedPreloads; |
639 for (auto resourceIter = resources->begin(); | 334 for (auto resourceIter = resources->begin(); |
640 resourceIter != resources->end(); resourceIter = resources->begin()) { | 335 resourceIter != resources->end(); resourceIter = resources->begin()) { |
641 DCHECK(resourceIter.get()); | 336 DCHECK(resourceIter.get()); |
642 DCHECK(resourceIter->value.get()); | 337 DCHECK(resourceIter->value.get()); |
643 DCHECK(resourceIter->value->resource()); | 338 DCHECK(resourceIter->value->resource()); |
644 if (policy == EvictAllResources || | 339 Resource* resource = resourceIter->value->resource(); |
645 !(resourceIter->value->resource() && | 340 DCHECK(resource); |
646 resourceIter->value->resource()->isUnusedPreload())) { | 341 if (policy != EvictAllResources && resource->isUnusedPreload()) { |
647 evict(resourceIter->value.get()); | |
648 } else { | |
649 // Store unused preloads aside, so they could be added back later. | 342 // Store unused preloads aside, so they could be added back later. |
650 // That is in order to avoid the performance impact of iterating over | 343 // That is in order to avoid the performance impact of iterating over |
651 // the same resource multiple times. | 344 // the same resource multiple times. |
652 unusedPreloads.append(resourceIter->value.get()); | 345 unusedPreloads.append(resourceIter->value.get()); |
653 resources->remove(resourceIter); | |
654 } | 346 } |
| 347 removeInternal(resources, resourceIter); |
655 } | 348 } |
656 for (const auto& unusedPreload : unusedPreloads) { | 349 for (const auto& unusedPreload : unusedPreloads) { |
657 KURL url = | 350 addInternal(resources, unusedPreload); |
658 removeFragmentIdentifierIfNeeded(unusedPreload->resource()->url()); | |
659 resources->set(url, unusedPreload); | |
660 } | 351 } |
661 // We may iterate multiple times over resourceMaps with unused preloads. | 352 // We may iterate multiple times over resourceMaps with unused preloads. |
662 // That's extremely unlikely to have any real-life performance impact. | 353 // That's extremely unlikely to have any real-life performance impact. |
663 if (!resources->size()) { | 354 if (!resources->size()) { |
664 m_resourceMaps.remove(resourceMapIter); | 355 m_resourceMaps.remove(resourceMapIter); |
665 resourceMapIter = m_resourceMaps.begin(); | 356 resourceMapIter = m_resourceMaps.begin(); |
666 } else { | 357 } else { |
667 ++resourceMapIter; | 358 ++resourceMapIter; |
668 } | 359 } |
669 } | 360 } |
670 } | 361 } |
671 | 362 |
672 void MemoryCache::prune() { | 363 void MemoryCache::prune() { |
673 TRACE_EVENT0("renderer", "MemoryCache::prune()"); | 364 TRACE_EVENT0("renderer", "MemoryCache::prune()"); |
674 | 365 |
675 if (m_inPruneResources) | 366 if (m_inPruneResources) |
676 return; | 367 return; |
677 if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && | 368 if (m_size <= m_capacity) // Fast path. |
678 m_deadSize <= m_maxDeadCapacity) // Fast path. | |
679 return; | 369 return; |
680 | 370 |
681 // To avoid burdening the current thread with repetitive pruning jobs, pruning | 371 // To avoid burdening the current thread with repetitive pruning jobs, pruning |
682 // is postponed until the end of the current task. If it has been more than | 372 // is postponed until the end of the current task. If it has been more than |
683 // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If | 373 // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If |
684 // the current thread's run loop is not active, then pruning will happen | 374 // the current thread's run loop is not active, then pruning will happen |
685 // immediately only if it has been over m_maxPruneDeferralDelay since the last | 375 // immediately only if it has been over m_maxPruneDeferralDelay since the last |
686 // prune. | 376 // prune. |
687 double currentTime = WTF::currentTime(); | 377 double currentTime = WTF::currentTime(); |
688 if (m_prunePending) { | 378 if (m_prunePending) { |
(...skipping 25 matching lines...) Expand all Loading... |
714 } | 404 } |
715 | 405 |
716 void MemoryCache::pruneNow(double currentTime, PruneStrategy strategy) { | 406 void MemoryCache::pruneNow(double currentTime, PruneStrategy strategy) { |
717 if (m_prunePending) { | 407 if (m_prunePending) { |
718 m_prunePending = false; | 408 m_prunePending = false; |
719 Platform::current()->currentThread()->removeTaskObserver(this); | 409 Platform::current()->currentThread()->removeTaskObserver(this); |
720 } | 410 } |
721 | 411 |
722 AutoReset<bool> reentrancyProtector(&m_inPruneResources, true); | 412 AutoReset<bool> reentrancyProtector(&m_inPruneResources, true); |
723 | 413 |
724 // Prune dead first, in case it was "borrowing" capacity from live. | 414 pruneResources(strategy); |
725 pruneDeadResources(strategy); | |
726 pruneLiveResources(strategy); | |
727 m_pruneFrameTimeStamp = m_lastFramePaintTimeStamp; | 415 m_pruneFrameTimeStamp = m_lastFramePaintTimeStamp; |
728 m_pruneTimeStamp = currentTime; | 416 m_pruneTimeStamp = currentTime; |
729 } | 417 } |
730 | 418 |
731 void MemoryCache::updateFramePaintTimestamp() { | 419 void MemoryCache::updateFramePaintTimestamp() { |
732 m_lastFramePaintTimeStamp = currentTime(); | 420 m_lastFramePaintTimeStamp = currentTime(); |
733 } | 421 } |
734 | 422 |
735 bool MemoryCache::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, | 423 bool MemoryCache::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, |
736 WebProcessMemoryDump* memoryDump) { | 424 WebProcessMemoryDump* memoryDump) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
769 resource->onMemoryDump(levelOfDetail, memoryDump); | 457 resource->onMemoryDump(levelOfDetail, memoryDump); |
770 } | 458 } |
771 } | 459 } |
772 return true; | 460 return true; |
773 } | 461 } |
774 | 462 |
775 void MemoryCache::onMemoryPressure(WebMemoryPressureLevel level) { | 463 void MemoryCache::onMemoryPressure(WebMemoryPressureLevel level) { |
776 pruneAll(); | 464 pruneAll(); |
777 } | 465 } |
778 | 466 |
779 bool MemoryCache::isInSameLRUListForTest(const Resource* x, const Resource* y) { | |
780 MemoryCacheEntry* ex = getEntryForResource(x); | |
781 MemoryCacheEntry* ey = getEntryForResource(y); | |
782 DCHECK(ex); | |
783 DCHECK(ey); | |
784 return lruListFor(ex->m_accessCount, x->size()) == | |
785 lruListFor(ey->m_accessCount, y->size()); | |
786 } | |
787 | |
788 } // namespace blink | 467 } // namespace blink |
OLD | NEW |