Chromium Code Reviews| 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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 45 #include "wtf/TemporaryChange.h" | 45 #include "wtf/TemporaryChange.h" |
| 46 #include "wtf/text/CString.h" | 46 #include "wtf/text/CString.h" |
| 47 | 47 |
| 48 using namespace std; | 48 using namespace std; |
| 49 | 49 |
| 50 namespace WebCore { | 50 namespace WebCore { |
| 51 | 51 |
| 52 static MemoryCache* gMemoryCache; | 52 static MemoryCache* gMemoryCache; |
| 53 | 53 |
| 54 static const int cDefaultCacheCapacity = 8192 * 1024; | 54 static const int cDefaultCacheCapacity = 8192 * 1024; |
| 55 static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. | |
|
Justin Novosad
2013/07/23 16:55:12
This variable should still exist, but it should be
| |
| 56 static const float cTargetPrunePercentage = .95f; // Percentage of capacity towa rd which we prune, to avoid immediately pruning again. | 55 static const float cTargetPrunePercentage = .95f; // Percentage of capacity towa rd which we prune, to avoid immediately pruning again. |
| 57 static const double cDefaultDecodedDataDeletionInterval = 0; | 56 static const double cDefaultDecodedDataDeletionInterval = 0; |
| 58 | 57 |
| 59 MemoryCache* memoryCache() | 58 MemoryCache* memoryCache() |
| 60 { | 59 { |
| 61 ASSERT(WTF::isMainThread()); | 60 ASSERT(WTF::isMainThread()); |
| 62 if (!gMemoryCache) | 61 if (!gMemoryCache) |
| 63 gMemoryCache = new MemoryCache(); | 62 gMemoryCache = new MemoryCache(); |
| 64 return gMemoryCache; | 63 return gMemoryCache; |
| 65 } | 64 } |
| 66 | 65 |
| 67 void setMemoryCacheForTesting(MemoryCache* memoryCache) | 66 void setMemoryCacheForTesting(MemoryCache* memoryCache) |
| 68 { | 67 { |
| 69 gMemoryCache = memoryCache; | 68 gMemoryCache = memoryCache; |
| 70 } | 69 } |
| 71 | 70 |
| 72 MemoryCache::MemoryCache() | 71 MemoryCache::MemoryCache() |
| 73 : m_inPruneResources(false) | 72 : m_inPruneResources(false) |
| 74 , m_capacity(cDefaultCacheCapacity) | 73 , m_capacity(cDefaultCacheCapacity) |
| 75 , m_minDeadCapacity(0) | 74 , m_minDeadCapacity(0) |
| 76 , m_maxDeadCapacity(cDefaultCacheCapacity) | 75 , m_maxDeadCapacity(cDefaultCacheCapacity) |
| 77 , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval) | 76 , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval) |
| 78 , m_liveSize(0) | 77 , m_liveSize(0) |
| 79 , m_deadSize(0) | 78 , m_deadSize(0) |
| 79 , m_delayBeforeLiveDecodedPrune(1) // Seconds. | |
| 80 #ifdef MEMORY_CACHE_STATS | 80 #ifdef MEMORY_CACHE_STATS |
| 81 , m_statsTimer(this, &MemoryCache::dumpStats) | 81 , m_statsTimer(this, &MemoryCache::dumpStats) |
| 82 #endif | 82 #endif |
| 83 { | 83 { |
| 84 #ifdef MEMORY_CACHE_STATS | 84 #ifdef MEMORY_CACHE_STATS |
| 85 const double statsIntervalInSeconds = 15; | 85 const double statsIntervalInSeconds = 15; |
| 86 m_statsTimer.startRepeating(statsIntervalInSeconds); | 86 m_statsTimer.startRepeating(statsIntervalInSeconds); |
| 87 #endif | 87 #endif |
| 88 } | 88 } |
| 89 | 89 |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 { | 179 { |
| 180 if (m_inPruneResources) | 180 if (m_inPruneResources) |
| 181 return; | 181 return; |
| 182 TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); | 182 TemporaryChange<bool> reentrancyProtector(m_inPruneResources, true); |
| 183 | 183 |
| 184 double currentTime = FrameView::currentPaintTimeStamp(); | 184 double currentTime = FrameView::currentPaintTimeStamp(); |
| 185 if (!currentTime) // In case prune is called directly, outside of a Frame pa int. | 185 if (!currentTime) // In case prune is called directly, outside of a Frame pa int. |
| 186 currentTime = WTF::currentTime(); | 186 currentTime = WTF::currentTime(); |
| 187 | 187 |
| 188 // Destroy any decoded data in live objects that we can. | 188 // Destroy any decoded data in live objects that we can. |
| 189 // Start from the tail, since this is the least recently accessed of the obj ects. | 189 // Start from the tail, since this is the lowest priority |
| 190 // and least recently accessed of the objects. | |
| 190 | 191 |
| 191 // The list might not be sorted by the m_lastDecodedAccessTime. The impact | 192 // The list might not be sorted by the m_lastDecodedAccessTime. The impact |
| 192 // of this weaker invariant is minor as the below if statement to check the | 193 // of this weaker invariant is minor as the below if statement to check the |
| 193 // elapsedTime will evaluate to false as the currentTime will be a lot | 194 // elapsedTime will evaluate to false as the currentTime will be a lot |
| 194 // greater than the current->m_lastDecodedAccessTime. | 195 // greater than the current->m_lastDecodedAccessTime. |
| 195 // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 | 196 // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 |
| 196 CachedResource* current = m_liveDecodedResources.m_tail; | |
| 197 while (current) { | |
| 198 CachedResource* prev = current->m_prevInLiveResourcesList; | |
| 199 ASSERT(current->hasClients()); | |
| 200 if (current->isLoaded() && current->decodedSize()) { | |
| 201 // Check to see if the remaining resources are too new to prune. | |
| 202 double elapsedTime = currentTime - current->m_lastDecodedAccessTime; | |
| 203 if (elapsedTime < cMinDelayBeforeLiveDecodedPrune) | |
| 204 return; | |
| 205 | 197 |
| 206 // Destroy our decoded data. This will remove us from | 198 // Start pruning from the lowest priority list. |
| 207 // m_liveDecodedResources, and possibly move us to a different LRU | 199 for (int i = 0; i < 3; i++) { |
| 208 // list in m_allResources. | 200 CachedResource* current = m_liveDecodedResources[i].m_tail; |
| 209 current->destroyDecodedData(); | 201 while (current) { |
| 202 CachedResource* prev = current->m_prevInLiveResourcesList; | |
| 203 ASSERT(current->hasClients()); | |
| 204 if (current->isLoaded() && current->decodedSize()) { | |
| 205 // Check to see if the remaining resources are too new to prune. | |
| 206 double elapsedTime = currentTime - current->m_lastDecodedAccessT ime; | |
| 207 if (elapsedTime < m_delayBeforeLiveDecodedPrune) | |
| 208 return; | |
| 210 | 209 |
| 211 if (targetSize && m_liveSize <= targetSize) | 210 // Destroy our decoded data. This will remove us from |
| 212 return; | 211 // m_liveDecodedResources, and possibly move us to a different L RU |
| 212 // list in m_allResources. | |
| 213 current->destroyDecodedData(); | |
| 214 | |
| 215 if (targetSize && m_liveSize <= targetSize) | |
| 216 return; | |
| 217 } | |
| 218 current = prev; | |
| 213 } | 219 } |
| 214 current = prev; | |
| 215 } | 220 } |
| 216 } | 221 } |
| 217 | 222 |
| 218 void MemoryCache::pruneDeadResources() | 223 void MemoryCache::pruneDeadResources() |
| 219 { | 224 { |
| 220 unsigned capacity = deadCapacity(); | 225 unsigned capacity = deadCapacity(); |
| 221 if (capacity && m_deadSize <= capacity) | 226 if (capacity && m_deadSize <= capacity) |
| 222 return; | 227 return; |
| 223 | 228 |
| 224 unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentag e); // Cut by a percentage to avoid immediately pruning again. | 229 unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentag e); // Cut by a percentage to avoid immediately pruning again. |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 432 | 437 |
| 433 } | 438 } |
| 434 | 439 |
| 435 void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource) | 440 void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource) |
| 436 { | 441 { |
| 437 // If we've never been accessed, then we're brand new and not in any list. | 442 // If we've never been accessed, then we're brand new and not in any list. |
| 438 if (!resource->m_inLiveDecodedResourcesList) | 443 if (!resource->m_inLiveDecodedResourcesList) |
| 439 return; | 444 return; |
| 440 resource->m_inLiveDecodedResourcesList = false; | 445 resource->m_inLiveDecodedResourcesList = false; |
| 441 | 446 |
| 447 LRUList* list = &m_liveDecodedResources[resource->decodePriority()]; | |
| 448 | |
| 442 #if !ASSERT_DISABLED | 449 #if !ASSERT_DISABLED |
| 443 // Verify that we are in fact in this list. | 450 // Verify that we are in fact in this list. |
| 444 bool found = false; | 451 bool found = false; |
| 445 for (CachedResource* current = m_liveDecodedResources.m_head; current; curre nt = current->m_nextInLiveResourcesList) { | 452 for (CachedResource* current = list->m_head; current; current = current->m_n extInLiveResourcesList) { |
| 446 if (current == resource) { | 453 if (current == resource) { |
| 447 found = true; | 454 found = true; |
| 448 break; | 455 break; |
| 449 } | 456 } |
| 450 } | 457 } |
| 451 ASSERT(found); | 458 ASSERT(found); |
| 452 #endif | 459 #endif |
| 453 | 460 |
| 454 CachedResource* next = resource->m_nextInLiveResourcesList; | 461 CachedResource* next = resource->m_nextInLiveResourcesList; |
| 455 CachedResource* prev = resource->m_prevInLiveResourcesList; | 462 CachedResource* prev = resource->m_prevInLiveResourcesList; |
| 456 | 463 |
| 457 if (next == 0 && prev == 0 && m_liveDecodedResources.m_head != resource) | 464 if (!next && !prev && list->m_head != resource) |
| 458 return; | 465 return; |
| 459 | 466 |
| 460 resource->m_nextInLiveResourcesList = 0; | 467 resource->m_nextInLiveResourcesList = 0; |
| 461 resource->m_prevInLiveResourcesList = 0; | 468 resource->m_prevInLiveResourcesList = 0; |
| 462 | 469 |
| 463 if (next) | 470 if (next) |
| 464 next->m_prevInLiveResourcesList = prev; | 471 next->m_prevInLiveResourcesList = prev; |
| 465 else if (m_liveDecodedResources.m_tail == resource) | 472 else if (list->m_tail == resource) |
| 466 m_liveDecodedResources.m_tail = prev; | 473 list->m_tail = prev; |
| 467 | 474 |
| 468 if (prev) | 475 if (prev) |
| 469 prev->m_nextInLiveResourcesList = next; | 476 prev->m_nextInLiveResourcesList = next; |
| 470 else if (m_liveDecodedResources.m_head == resource) | 477 else if (list->m_head == resource) |
| 471 m_liveDecodedResources.m_head = next; | 478 list->m_head = next; |
| 472 } | 479 } |
| 473 | 480 |
| 474 void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource) | 481 void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource) |
| 475 { | 482 { |
| 476 // Make sure we aren't in the list already. | 483 // Make sure we aren't in the list already. |
| 477 ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResour cesList && !resource->m_inLiveDecodedResourcesList); | 484 ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResour cesList && !resource->m_inLiveDecodedResourcesList); |
| 478 resource->m_inLiveDecodedResourcesList = true; | 485 resource->m_inLiveDecodedResourcesList = true; |
| 479 | 486 |
| 480 resource->m_nextInLiveResourcesList = m_liveDecodedResources.m_head; | 487 LRUList* list = &m_liveDecodedResources[resource->decodePriority()]; |
| 481 if (m_liveDecodedResources.m_head) | 488 resource->m_nextInLiveResourcesList = list->m_head; |
| 482 m_liveDecodedResources.m_head->m_prevInLiveResourcesList = resource; | 489 if (list->m_head) |
| 483 m_liveDecodedResources.m_head = resource; | 490 list->m_head->m_prevInLiveResourcesList = resource; |
| 484 | 491 list->m_head = resource; |
| 492 | |
| 485 if (!resource->m_nextInLiveResourcesList) | 493 if (!resource->m_nextInLiveResourcesList) |
| 486 m_liveDecodedResources.m_tail = resource; | 494 list->m_tail = resource; |
| 487 | 495 |
| 488 #if !ASSERT_DISABLED | 496 #if !ASSERT_DISABLED |
| 489 // Verify that we are in now in the list like we should be. | 497 // Verify that we are in now in the list like we should be. |
| 490 bool found = false; | 498 bool found = false; |
| 491 for (CachedResource* current = m_liveDecodedResources.m_head; current; curre nt = current->m_nextInLiveResourcesList) { | 499 for (CachedResource* current = list->m_head; current; current = current->m_n extInLiveResourcesList) { |
| 492 if (current == resource) { | 500 if (current == resource) { |
| 493 found = true; | 501 found = true; |
| 494 break; | 502 break; |
| 495 } | 503 } |
| 496 } | 504 } |
| 497 ASSERT(found); | 505 ASSERT(found); |
| 498 #endif | 506 #endif |
| 499 | 507 |
| 500 } | 508 } |
| 501 | 509 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 582 } | 590 } |
| 583 return stats; | 591 return stats; |
| 584 } | 592 } |
| 585 | 593 |
| 586 void MemoryCache::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const | 594 void MemoryCache::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| 587 { | 595 { |
| 588 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::MemoryCache Structures); | 596 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::MemoryCache Structures); |
| 589 memoryObjectInfo->setClassName("MemoryCache"); | 597 memoryObjectInfo->setClassName("MemoryCache"); |
| 590 info.addMember(m_resources, "resources"); | 598 info.addMember(m_resources, "resources"); |
| 591 info.addMember(m_allResources, "allResources"); | 599 info.addMember(m_allResources, "allResources"); |
| 592 info.addMember(m_liveDecodedResources, "liveDecodedResources"); | 600 for (int i = 0; i < 3; ++i) |
| 601 info.addMember(m_liveDecodedResources[i], "liveDecodedResources"); | |
| 593 for (CachedResourceMap::const_iterator i = m_resources.begin(); i != m_resou rces.end(); ++i) | 602 for (CachedResourceMap::const_iterator i = m_resources.begin(); i != m_resou rces.end(); ++i) |
| 594 info.addMember(i->value, "cachedResourceItem", WTF::RetainingPointer); | 603 info.addMember(i->value, "cachedResourceItem", WTF::RetainingPointer); |
| 595 } | 604 } |
| 596 | 605 |
| 597 void MemoryCache::evictResources() | 606 void MemoryCache::evictResources() |
| 598 { | 607 { |
| 599 for (;;) { | 608 for (;;) { |
| 600 CachedResourceMap::iterator i = m_resources.begin(); | 609 CachedResourceMap::iterator i = m_resources.begin(); |
| 601 if (i == m_resources.end()) | 610 if (i == m_resources.end()) |
| 602 break; | 611 break; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 658 printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSiz e() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, cur rent->accessCount(), current->hasClients(), current->isPurgeable(), current->was Purged()); | 667 printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSiz e() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, cur rent->accessCount(), current->hasClients(), current->isPurgeable(), current->was Purged()); |
| 659 | 668 |
| 660 current = prev; | 669 current = prev; |
| 661 } | 670 } |
| 662 } | 671 } |
| 663 } | 672 } |
| 664 | 673 |
| 665 #endif // MEMORY_CACHE_STATS | 674 #endif // MEMORY_CACHE_STATS |
| 666 | 675 |
| 667 } // namespace WebCore | 676 } // namespace WebCore |
| OLD | NEW |