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

Side by Side Diff: third_party/WebKit/Source/core/fetch/MemoryCache.cpp

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

Powered by Google App Engine
This is Rietveld 408576698