This source file includes following definitions.
- GetDefaultCacheSize
- RegisterPrefs
- GetInstance
- weak_factory_
- Add
- Remove
- ObserveActivity
- ObserveStats
- SetGlobalSizeLimit
- ClearCache
- ClearCacheOnNavigation
- Observe
- GetDefaultGlobalSizeLimit
- GatherStats
- GetSize
- AttemptTactic
- AddToStrategy
- EnactStrategy
- ClearRendererCache
- ReviseAllocationStrategy
- ReviseAllocationStrategyLater
- FindInactiveRenderers
#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"
#if defined(OS_ANDROID)
#include "base/android/sys_utils.h"
#endif
using base::Time;
using base::TimeDelta;
using blink::WebCache;
static const int kReviseAllocationDelayMS = 200;
static const int kDefaultMemoryCacheSize = 8 * 1024 * 1024;
namespace {
int GetDefaultCacheSize() {
int default_cache_size = kDefaultMemoryCacheSize;
int mem_size_mb = base::SysInfo::AmountOfPhysicalMemoryMB();
if (mem_size_mb >= 1000)
default_cache_size *= 4;
else if (mem_size_mb >= 512)
default_cache_size *= 2;
UMA_HISTOGRAM_MEMORY_MB("Cache.MaxCacheSizeMB",
default_cache_size / 1024 / 1024);
return default_cache_size;
}
}
void WebCacheManager::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(prefs::kMemoryCacheSize, GetDefaultCacheSize());
}
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);
active_renderers_.insert(renderer_id);
RendererInfo* stats = &(stats_[renderer_id]);
memset(stats, 0, sizeof(*stats));
stats->access = Time::Now();
ReviseAllocationStrategyLater();
}
void WebCacheManager::Remove(int renderer_id) {
active_renderers_.erase(renderer_id);
inactive_renderers_.erase(renderer_id);
stats_.erase(renderer_id);
ReviseAllocationStrategyLater();
}
void WebCacheManager::ObserveActivity(int renderer_id) {
StatsMap::iterator item = stats_.find(renderer_id);
if (item == stats_.end())
return;
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);
ReviseAllocationStrategyLater();
}
}
void WebCacheManager::ObserveStats(int renderer_id,
const WebCache::UsageStats& stats) {
StatsMap::iterator entry = stats_.find(renderer_id);
if (entry == stats_.end())
return;
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() {
ClearRendererCache(active_renderers_, INSTANTLY);
ClearRendererCache(inactive_renderers_, INSTANTLY);
}
void WebCacheManager::ClearCacheOnNavigation() {
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;
}
}
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;
}
}
size_t WebCacheManager::GetSize(AllocationTactic tactic,
const WebCache::UsageStats& stats) {
switch (tactic) {
case DIVIDE_EVENLY:
return 0;
case KEEP_CURRENT_WITH_HEADROOM:
return 3 * GetSize(KEEP_CURRENT, stats) / 2;
case KEEP_CURRENT:
return stats.liveSize + stats.deadSize;
case KEEP_LIVE_WITH_HEADROOM:
return 3 * GetSize(KEEP_LIVE, stats) / 2;
case KEEP_LIVE:
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);
if (global_size_limit_ < active_size + inactive_size)
return false;
size_t total_extra = global_size_limit_ - (active_size + inactive_size);
size_t shares = active_renderers_.size();
size_t inactive_extra = 0;
if (!inactive_renderers_.empty()) {
++shares;
inactive_extra = total_extra / shares;
}
size_t active_extra = total_extra - inactive_extra;
AddToStrategy(active_renderers_, active_tactic, active_extra, strategy);
AddToStrategy(inactive_renderers_, inactive_tactic, inactive_extra, strategy);
return true;
}
void WebCacheManager::AddToStrategy(const std::set<int>& renderers,
AllocationTactic tactic,
size_t extra_bytes_to_allocate,
AllocationStrategy* strategy) {
DCHECK(strategy);
if (renderers.empty())
return;
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;
StatsMap::iterator elmt = stats_.find(*iter);
if (elmt != stats_.end())
cache_size += GetSize(tactic, elmt->second);
strategy->push_back(Allocation(*iter, cache_size));
++iter;
}
}
void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) {
AllocationStrategy::const_iterator allocation = strategy.begin();
while (allocation != strategy.end()) {
content::RenderProcessHost* host =
content::RenderProcessHost::FromID(allocation->first);
if (host) {
size_t capacity = allocation->second;
size_t min_dead_capacity = 0;
size_t max_dead_capacity = capacity / 2;
#if defined(OS_ANDROID)
if (base::android::SysUtils::IsLowEndDevice())
max_dead_capacity = std::min(static_cast<size_t>(512 * 1024),
max_dead_capacity);
#endif
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());
FindInactiveRenderers();
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);
AllocationStrategy strategy;
if (
AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
KEEP_CURRENT, inactive, &strategy) ||
AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
KEEP_LIVE, inactive, &strategy) ||
AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active,
DIVIDE_EVENLY, inactive, &strategy) ||
AttemptTactic(KEEP_LIVE, active, DIVIDE_EVENLY, inactive, &strategy) ||
AttemptTactic(DIVIDE_EVENLY, active, DIVIDE_EVENLY, inactive,
&strategy)) {
EnactStrategy(strategy);
} else {
NOTREACHED() << "Unable to find a cache allocation";
}
}
void WebCacheManager::ReviseAllocationStrategyLater() {
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)) {
inactive_renderers_.insert(*iter);
active_renderers_.erase(*iter);
iter = active_renderers_.begin();
continue;
}
++iter;
}
}