| Index: chrome/browser/renderer_host/web_cache_manager.cc
|
| diff --git a/chrome/browser/renderer_host/web_cache_manager.cc b/chrome/browser/renderer_host/web_cache_manager.cc
|
| deleted file mode 100644
|
| index a61a2c3eaaafc35043c5d938b1e5057d895b1fe5..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/renderer_host/web_cache_manager.cc
|
| +++ /dev/null
|
| @@ -1,444 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/renderer_host/web_cache_manager.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/memory/singleton.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/prefs/pref_registry_simple.h"
|
| -#include "base/prefs/pref_service.h"
|
| -#include "base/sys_info.h"
|
| -#include "base/time/time.h"
|
| -#include "chrome/browser/browser_process.h"
|
| -#include "chrome/browser/chrome_notification_types.h"
|
| -#include "chrome/common/chrome_constants.h"
|
| -#include "chrome/common/pref_names.h"
|
| -#include "chrome/common/render_messages.h"
|
| -#include "content/public/browser/notification_service.h"
|
| -#include "content/public/browser/render_process_host.h"
|
| -
|
| -using base::Time;
|
| -using base::TimeDelta;
|
| -using blink::WebCache;
|
| -
|
| -static const int kReviseAllocationDelayMS = 200;
|
| -
|
| -// The default size limit of the in-memory cache is 8 MB
|
| -static const int kDefaultMemoryCacheSize = 8 * 1024 * 1024;
|
| -
|
| -namespace {
|
| -
|
| -int GetDefaultCacheSize() {
|
| - // Start off with a modest default
|
| - int default_cache_size = kDefaultMemoryCacheSize;
|
| -
|
| - // Check how much physical memory the OS has
|
| - int mem_size_mb = base::SysInfo::AmountOfPhysicalMemoryMB();
|
| - if (mem_size_mb >= 1000) // If we have a GB of memory, set a larger default.
|
| - default_cache_size *= 4;
|
| - else if (mem_size_mb >= 512) // With 512 MB, set a slightly larger default.
|
| - default_cache_size *= 2;
|
| -
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.MaxCacheSizeMB",
|
| - default_cache_size / 1024 / 1024);
|
| -
|
| - return default_cache_size;
|
| -}
|
| -
|
| -} // anonymous namespace
|
| -
|
| -// static
|
| -void WebCacheManager::RegisterPrefs(PrefRegistrySimple* registry) {
|
| - registry->RegisterIntegerPref(prefs::kMemoryCacheSize, GetDefaultCacheSize());
|
| -}
|
| -
|
| -// static
|
| -WebCacheManager* WebCacheManager::GetInstance() {
|
| - return Singleton<WebCacheManager>::get();
|
| -}
|
| -
|
| -WebCacheManager::WebCacheManager()
|
| - : global_size_limit_(GetDefaultGlobalSizeLimit()),
|
| - weak_factory_(this) {
|
| - registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
|
| - content::NotificationService::AllBrowserContextsAndSources());
|
| - registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
|
| - content::NotificationService::AllBrowserContextsAndSources());
|
| -}
|
| -
|
| -WebCacheManager::~WebCacheManager() {
|
| -}
|
| -
|
| -void WebCacheManager::Add(int renderer_id) {
|
| - DCHECK(inactive_renderers_.count(renderer_id) == 0);
|
| -
|
| - // It is tempting to make the following DCHECK here, but it fails when a new
|
| - // tab is created as we observe activity from that tab because the
|
| - // RenderProcessHost is recreated and adds itself.
|
| - //
|
| - // DCHECK(active_renderers_.count(renderer_id) == 0);
|
| - //
|
| - // However, there doesn't seem to be much harm in receiving the calls in this
|
| - // order.
|
| -
|
| - active_renderers_.insert(renderer_id);
|
| -
|
| - RendererInfo* stats = &(stats_[renderer_id]);
|
| - memset(stats, 0, sizeof(*stats));
|
| - stats->access = Time::Now();
|
| -
|
| - // Revise our allocation strategy to account for this new renderer.
|
| - ReviseAllocationStrategyLater();
|
| -}
|
| -
|
| -void WebCacheManager::Remove(int renderer_id) {
|
| - // Erase all knowledge of this renderer
|
| - active_renderers_.erase(renderer_id);
|
| - inactive_renderers_.erase(renderer_id);
|
| - stats_.erase(renderer_id);
|
| -
|
| - // Reallocate the resources used by this renderer
|
| - ReviseAllocationStrategyLater();
|
| -}
|
| -
|
| -void WebCacheManager::ObserveActivity(int renderer_id) {
|
| - StatsMap::iterator item = stats_.find(renderer_id);
|
| - if (item == stats_.end())
|
| - return; // We might see stats for a renderer that has been destroyed.
|
| -
|
| - // Record activity.
|
| - active_renderers_.insert(renderer_id);
|
| - item->second.access = Time::Now();
|
| -
|
| - std::set<int>::iterator elmt = inactive_renderers_.find(renderer_id);
|
| - if (elmt != inactive_renderers_.end()) {
|
| - inactive_renderers_.erase(elmt);
|
| -
|
| - // A renderer that was inactive, just became active. We should make sure
|
| - // it is given a fair cache allocation, but we defer this for a bit in
|
| - // order to make this function call cheap.
|
| - ReviseAllocationStrategyLater();
|
| - }
|
| -}
|
| -
|
| -void WebCacheManager::ObserveStats(int renderer_id,
|
| - const WebCache::UsageStats& stats) {
|
| - StatsMap::iterator entry = stats_.find(renderer_id);
|
| - if (entry == stats_.end())
|
| - return; // We might see stats for a renderer that has been destroyed.
|
| -
|
| - // Record the updated stats.
|
| - entry->second.capacity = stats.capacity;
|
| - entry->second.deadSize = stats.deadSize;
|
| - entry->second.liveSize = stats.liveSize;
|
| - entry->second.maxDeadCapacity = stats.maxDeadCapacity;
|
| - entry->second.minDeadCapacity = stats.minDeadCapacity;
|
| -}
|
| -
|
| -void WebCacheManager::SetGlobalSizeLimit(size_t bytes) {
|
| - global_size_limit_ = bytes;
|
| - ReviseAllocationStrategyLater();
|
| -}
|
| -
|
| -void WebCacheManager::ClearCache() {
|
| - // Tell each renderer process to clear the cache.
|
| - ClearRendererCache(active_renderers_, INSTANTLY);
|
| - ClearRendererCache(inactive_renderers_, INSTANTLY);
|
| -}
|
| -
|
| -void WebCacheManager::ClearCacheOnNavigation() {
|
| - // Tell each renderer process to clear the cache when a tab is reloaded or
|
| - // the user navigates to a new website.
|
| - ClearRendererCache(active_renderers_, ON_NAVIGATION);
|
| - ClearRendererCache(inactive_renderers_, ON_NAVIGATION);
|
| -}
|
| -
|
| -void WebCacheManager::Observe(int type,
|
| - const content::NotificationSource& source,
|
| - const content::NotificationDetails& details) {
|
| - switch (type) {
|
| - case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
|
| - content::RenderProcessHost* process =
|
| - content::Source<content::RenderProcessHost>(source).ptr();
|
| - Add(process->GetID());
|
| - break;
|
| - }
|
| - case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
|
| - content::RenderProcessHost* process =
|
| - content::Source<content::RenderProcessHost>(source).ptr();
|
| - Remove(process->GetID());
|
| - break;
|
| - }
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -}
|
| -
|
| -// static
|
| -size_t WebCacheManager::GetDefaultGlobalSizeLimit() {
|
| - PrefService* perf_service = g_browser_process->local_state();
|
| - if (perf_service)
|
| - return perf_service->GetInteger(prefs::kMemoryCacheSize);
|
| -
|
| - return GetDefaultCacheSize();
|
| -}
|
| -
|
| -void WebCacheManager::GatherStats(const std::set<int>& renderers,
|
| - WebCache::UsageStats* stats) {
|
| - DCHECK(stats);
|
| -
|
| - memset(stats, 0, sizeof(WebCache::UsageStats));
|
| -
|
| - std::set<int>::const_iterator iter = renderers.begin();
|
| - while (iter != renderers.end()) {
|
| - StatsMap::iterator elmt = stats_.find(*iter);
|
| - if (elmt != stats_.end()) {
|
| - stats->minDeadCapacity += elmt->second.minDeadCapacity;
|
| - stats->maxDeadCapacity += elmt->second.maxDeadCapacity;
|
| - stats->capacity += elmt->second.capacity;
|
| - stats->liveSize += elmt->second.liveSize;
|
| - stats->deadSize += elmt->second.deadSize;
|
| - }
|
| - ++iter;
|
| - }
|
| -}
|
| -
|
| -// static
|
| -size_t WebCacheManager::GetSize(AllocationTactic tactic,
|
| - const WebCache::UsageStats& stats) {
|
| - switch (tactic) {
|
| - case DIVIDE_EVENLY:
|
| - // We aren't going to reserve any space for existing objects.
|
| - return 0;
|
| - case KEEP_CURRENT_WITH_HEADROOM:
|
| - // We need enough space for our current objects, plus some headroom.
|
| - return 3 * GetSize(KEEP_CURRENT, stats) / 2;
|
| - case KEEP_CURRENT:
|
| - // We need enough space to keep our current objects.
|
| - return stats.liveSize + stats.deadSize;
|
| - case KEEP_LIVE_WITH_HEADROOM:
|
| - // We need enough space to keep out live resources, plus some headroom.
|
| - return 3 * GetSize(KEEP_LIVE, stats) / 2;
|
| - case KEEP_LIVE:
|
| - // We need enough space to keep our live resources.
|
| - return stats.liveSize;
|
| - default:
|
| - NOTREACHED() << "Unknown cache allocation tactic";
|
| - return 0;
|
| - }
|
| -}
|
| -
|
| -bool WebCacheManager::AttemptTactic(
|
| - AllocationTactic active_tactic,
|
| - const WebCache::UsageStats& active_stats,
|
| - AllocationTactic inactive_tactic,
|
| - const WebCache::UsageStats& inactive_stats,
|
| - AllocationStrategy* strategy) {
|
| - DCHECK(strategy);
|
| -
|
| - size_t active_size = GetSize(active_tactic, active_stats);
|
| - size_t inactive_size = GetSize(inactive_tactic, inactive_stats);
|
| -
|
| - // Give up if we don't have enough space to use this tactic.
|
| - if (global_size_limit_ < active_size + inactive_size)
|
| - return false;
|
| -
|
| - // Compute the unreserved space available.
|
| - size_t total_extra = global_size_limit_ - (active_size + inactive_size);
|
| -
|
| - // The plan for the extra space is to divide it evenly amoung the active
|
| - // renderers.
|
| - size_t shares = active_renderers_.size();
|
| -
|
| - // The inactive renderers get one share of the extra memory to be divided
|
| - // among themselves.
|
| - size_t inactive_extra = 0;
|
| - if (!inactive_renderers_.empty()) {
|
| - ++shares;
|
| - inactive_extra = total_extra / shares;
|
| - }
|
| -
|
| - // The remaining memory is allocated to the active renderers.
|
| - size_t active_extra = total_extra - inactive_extra;
|
| -
|
| - // Actually compute the allocations for each renderer.
|
| - AddToStrategy(active_renderers_, active_tactic, active_extra, strategy);
|
| - AddToStrategy(inactive_renderers_, inactive_tactic, inactive_extra, strategy);
|
| -
|
| - // We succeeded in computing an allocation strategy.
|
| - return true;
|
| -}
|
| -
|
| -void WebCacheManager::AddToStrategy(const std::set<int>& renderers,
|
| - AllocationTactic tactic,
|
| - size_t extra_bytes_to_allocate,
|
| - AllocationStrategy* strategy) {
|
| - DCHECK(strategy);
|
| -
|
| - // Nothing to do if there are no renderers. It is common for there to be no
|
| - // inactive renderers if there is a single active tab.
|
| - if (renderers.empty())
|
| - return;
|
| -
|
| - // Divide the extra memory evenly among the renderers.
|
| - size_t extra_each = extra_bytes_to_allocate / renderers.size();
|
| -
|
| - std::set<int>::const_iterator iter = renderers.begin();
|
| - while (iter != renderers.end()) {
|
| - size_t cache_size = extra_each;
|
| -
|
| - // Add in the space required to implement |tactic|.
|
| - StatsMap::iterator elmt = stats_.find(*iter);
|
| - if (elmt != stats_.end())
|
| - cache_size += GetSize(tactic, elmt->second);
|
| -
|
| - // Record the allocation in our strategy.
|
| - strategy->push_back(Allocation(*iter, cache_size));
|
| - ++iter;
|
| - }
|
| -}
|
| -
|
| -void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) {
|
| - // Inform each render process of its cache allocation.
|
| - AllocationStrategy::const_iterator allocation = strategy.begin();
|
| - while (allocation != strategy.end()) {
|
| - content::RenderProcessHost* host =
|
| - content::RenderProcessHost::FromID(allocation->first);
|
| - if (host) {
|
| - // This is the capacity this renderer has been allocated.
|
| - size_t capacity = allocation->second;
|
| -
|
| - // We don't reserve any space for dead objects in the cache. Instead, we
|
| - // prefer to keep live objects around. There is probably some performance
|
| - // tuning to be done here.
|
| - size_t min_dead_capacity = 0;
|
| -
|
| - // We allow the dead objects to consume up to half of the cache capacity.
|
| - size_t max_dead_capacity = capacity / 2;
|
| - if (base::SysInfo::IsLowEndDevice()) {
|
| - max_dead_capacity = std::min(static_cast<size_t>(512 * 1024),
|
| - max_dead_capacity);
|
| - }
|
| - host->Send(new ChromeViewMsg_SetCacheCapacities(min_dead_capacity,
|
| - max_dead_capacity,
|
| - capacity));
|
| - }
|
| - ++allocation;
|
| - }
|
| -}
|
| -
|
| -void WebCacheManager::ClearRendererCache(
|
| - const std::set<int>& renderers,
|
| - WebCacheManager::ClearCacheOccasion occasion) {
|
| - std::set<int>::const_iterator iter = renderers.begin();
|
| - for (; iter != renderers.end(); ++iter) {
|
| - content::RenderProcessHost* host =
|
| - content::RenderProcessHost::FromID(*iter);
|
| - if (host)
|
| - host->Send(new ChromeViewMsg_ClearCache(occasion == ON_NAVIGATION));
|
| - }
|
| -}
|
| -
|
| -void WebCacheManager::ReviseAllocationStrategy() {
|
| - DCHECK(stats_.size() <=
|
| - active_renderers_.size() + inactive_renderers_.size());
|
| -
|
| - // Check if renderers have gone inactive.
|
| - FindInactiveRenderers();
|
| -
|
| - // Gather statistics
|
| - WebCache::UsageStats active;
|
| - WebCache::UsageStats inactive;
|
| - GatherStats(active_renderers_, &active);
|
| - GatherStats(inactive_renderers_, &inactive);
|
| -
|
| - UMA_HISTOGRAM_COUNTS_100("Cache.ActiveTabs", active_renderers_.size());
|
| - UMA_HISTOGRAM_COUNTS_100("Cache.InactiveTabs", inactive_renderers_.size());
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveCapacityMB",
|
| - active.capacity / 1024 / 1024);
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveDeadSizeMB",
|
| - active.deadSize / 1024 / 1024);
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB",
|
| - active.liveSize / 1024 / 1024);
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveCapacityMB",
|
| - inactive.capacity / 1024 / 1024);
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveDeadSizeMB",
|
| - inactive.deadSize / 1024 / 1024);
|
| - UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveLiveSizeMB",
|
| - inactive.liveSize / 1024 / 1024);
|
| -
|
| - // Compute an allocation strategy.
|
| - //
|
| - // We attempt various tactics in order of preference. Our first preference
|
| - // is not to evict any objects. If we don't have enough resources, we'll
|
| - // first try to evict dead data only. If that fails, we'll just divide the
|
| - // resources we have evenly.
|
| - //
|
| - // We always try to give the active renderers some head room in their
|
| - // allocations so they can take memory away from an inactive renderer with
|
| - // a large cache allocation.
|
| - //
|
| - // Notice the early exit will prevent attempting less desirable tactics once
|
| - // we've found a workable strategy.
|
| - AllocationStrategy strategy;
|
| - if ( // Ideally, we'd like to give the active renderers some headroom and
|
| - // keep all our current objects.
|
| - AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
|
| - KEEP_CURRENT, inactive, &strategy) ||
|
| - // If we can't have that, then we first try to evict the dead objects in
|
| - // the caches of inactive renderers.
|
| - AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
|
| - KEEP_LIVE, inactive, &strategy) ||
|
| - // Next, we try to keep the live objects in the active renders (with some
|
| - // room for new objects) and give whatever is left to the inactive
|
| - // renderers.
|
| - AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active,
|
| - DIVIDE_EVENLY, inactive, &strategy) ||
|
| - // If we've gotten this far, then we are very tight on memory. Let's try
|
| - // to at least keep around the live objects for the active renderers.
|
| - AttemptTactic(KEEP_LIVE, active, DIVIDE_EVENLY, inactive, &strategy) ||
|
| - // We're basically out of memory. The best we can do is just divide up
|
| - // what we have and soldier on.
|
| - AttemptTactic(DIVIDE_EVENLY, active, DIVIDE_EVENLY, inactive,
|
| - &strategy)) {
|
| - // Having found a workable strategy, we enact it.
|
| - EnactStrategy(strategy);
|
| - } else {
|
| - // DIVIDE_EVENLY / DIVIDE_EVENLY should always succeed.
|
| - NOTREACHED() << "Unable to find a cache allocation";
|
| - }
|
| -}
|
| -
|
| -void WebCacheManager::ReviseAllocationStrategyLater() {
|
| - // Ask to be called back in a few milliseconds to actually recompute our
|
| - // allocation.
|
| - base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| - base::Bind(
|
| - &WebCacheManager::ReviseAllocationStrategy,
|
| - weak_factory_.GetWeakPtr()),
|
| - base::TimeDelta::FromMilliseconds(kReviseAllocationDelayMS));
|
| -}
|
| -
|
| -void WebCacheManager::FindInactiveRenderers() {
|
| - std::set<int>::const_iterator iter = active_renderers_.begin();
|
| - while (iter != active_renderers_.end()) {
|
| - StatsMap::iterator elmt = stats_.find(*iter);
|
| - DCHECK(elmt != stats_.end());
|
| - TimeDelta idle = Time::Now() - elmt->second.access;
|
| - if (idle >= TimeDelta::FromMinutes(kRendererInactiveThresholdMinutes)) {
|
| - // Moved to inactive status. This invalidates our iterator.
|
| - inactive_renderers_.insert(*iter);
|
| - active_renderers_.erase(*iter);
|
| - iter = active_renderers_.begin();
|
| - continue;
|
| - }
|
| - ++iter;
|
| - }
|
| -}
|
|
|