| Index: chrome/browser/oom_priority_manager.cc
|
| diff --git a/chrome/browser/oom_priority_manager.cc b/chrome/browser/oom_priority_manager.cc
|
| index 182cbac09aaaed4f5e1f2806e6d77699cedab131..fb46fc7f3317b60a9ecee1f188b98d3109a2a915 100644
|
| --- a/chrome/browser/oom_priority_manager.cc
|
| +++ b/chrome/browser/oom_priority_manager.cc
|
| @@ -9,10 +9,12 @@
|
|
|
| #include "base/process.h"
|
| #include "base/process_util.h"
|
| +#include "base/string_number_conversions.h"
|
| #include "base/string16.h"
|
| #include "base/synchronization/lock.h"
|
| #include "base/threading/thread.h"
|
| #include "base/timer.h"
|
| +#include "base/utf_string_conversions.h"
|
| #include "build/build_config.h"
|
| #include "chrome/browser/tabs/tab_strip_model.h"
|
| #include "chrome/browser/ui/browser_list.h"
|
| @@ -23,6 +25,7 @@
|
| #include "content/browser/renderer_host/render_widget_host.h"
|
| #include "content/browser/tab_contents/tab_contents.h"
|
| #include "content/browser/zygote_host_linux.h"
|
| +#include "content/common/content_notification_types.h"
|
| #include "content/common/notification_service.h"
|
|
|
| #if !defined(OS_CHROMEOS)
|
| @@ -34,33 +37,38 @@ using base::TimeTicks;
|
| using base::ProcessHandle;
|
| using base::ProcessMetrics;
|
|
|
| +namespace {
|
| +
|
| +// Returns a unique ID for a TabContents. Do not cast back to a pointer, as
|
| +// the TabContents could be deleted if the user closed the tab.
|
| +int64 IdFromTabContents(TabContents* tab_contents) {
|
| + return reinterpret_cast<int64>(tab_contents);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| namespace browser {
|
|
|
| // The default interval in seconds after which to adjust the oom_score_adj
|
| // value.
|
| #define ADJUSTMENT_INTERVAL_SECONDS 10
|
|
|
| -// The default interval in minutes for considering activation times
|
| -// "equal".
|
| -#define BUCKET_INTERVAL_MINUTES 10
|
| -
|
| // The default interval in milliseconds to wait before setting the score of
|
| // currently focused tab.
|
| #define FOCUSED_TAB_SCORE_ADJUST_INTERVAL_MS 500
|
|
|
| -OomPriorityManager::RendererStats::RendererStats()
|
| +OomPriorityManager::TabStats::TabStats()
|
| : is_pinned(false),
|
| is_selected(false),
|
| - memory_used(0),
|
| - renderer_handle(0) {
|
| + renderer_handle(0),
|
| + tab_contents_id(0) {
|
| }
|
|
|
| -OomPriorityManager::RendererStats::~RendererStats() {
|
| +OomPriorityManager::TabStats::~TabStats() {
|
| }
|
|
|
| OomPriorityManager::OomPriorityManager()
|
| : focused_tab_pid_(0) {
|
| - renderer_stats_.reserve(32); // 99% of users have < 30 tabs open
|
| registrar_.Add(this,
|
| content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
|
| NotificationService::AllSources());
|
| @@ -90,25 +98,48 @@ void OomPriorityManager::Stop() {
|
| }
|
|
|
| std::vector<string16> OomPriorityManager::GetTabTitles() {
|
| - base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
|
| + TabStatsList stats = GetTabStatsOnUIThread();
|
| + base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
|
| std::vector<string16> titles;
|
| - titles.reserve(renderer_stats_.size());
|
| - StatsList::iterator it = renderer_stats_.begin();
|
| - for ( ; it != renderer_stats_.end(); ++it) {
|
| - titles.push_back(it->title);
|
| + titles.reserve(stats.size());
|
| + TabStatsList::iterator it = stats.begin();
|
| + for ( ; it != stats.end(); ++it) {
|
| + string16 str = it->title;
|
| + str += ASCIIToUTF16(" (");
|
| + int score = pid_to_oom_score_[it->renderer_handle];
|
| + str += base::IntToString16(score);
|
| + str += ASCIIToUTF16(")");
|
| + titles.push_back(str);
|
| }
|
| return titles;
|
| }
|
|
|
| +void OomPriorityManager::DiscardTab() {
|
| + TabStatsList stats = GetTabStatsOnUIThread();
|
| + if (stats.empty())
|
| + return;
|
| + std::sort(stats.begin(), stats.end(), CompareTabStats);
|
| + TabStatsList::const_reverse_iterator rit = stats.rbegin();
|
| + int64 least_important_tab_id = rit->tab_contents_id;
|
| + for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
|
| + browser_iterator != BrowserList::end(); ++browser_iterator) {
|
| + Browser* browser = *browser_iterator;
|
| + TabStripModel* model = browser->tabstrip_model();
|
| + for (int idx = 0; idx < model->count(); idx++) {
|
| + TabContents* tab_contents = model->GetTabContentsAt(idx)->tab_contents();
|
| + int64 tab_contents_id = IdFromTabContents(tab_contents);
|
| + if (tab_contents_id == least_important_tab_id) {
|
| + model->CloseTabContentsAt(idx,
|
| + TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| // Returns true if |first| is considered less desirable to be killed
|
| // than |second|.
|
| -bool OomPriorityManager::CompareRendererStats(RendererStats first,
|
| - RendererStats second) {
|
| - // The size of the slop in comparing activation times. [This is
|
| - // allocated here to avoid static initialization at startup time.]
|
| - static const int64 kTimeBucketInterval =
|
| - TimeDelta::FromMinutes(BUCKET_INTERVAL_MINUTES).ToInternalValue();
|
| -
|
| +bool OomPriorityManager::CompareTabStats(TabStats first,
|
| + TabStats second) {
|
| // Being currently selected is most important.
|
| if (first.is_selected != second.is_selected)
|
| return first.is_selected == true;
|
| @@ -117,18 +148,12 @@ bool OomPriorityManager::CompareRendererStats(RendererStats first,
|
| if (first.is_pinned != second.is_pinned)
|
| return first.is_pinned == true;
|
|
|
| - // We want to be a little "fuzzy" when we compare these, because
|
| - // it's not really possible for the times to be identical, but if
|
| - // the user selected two tabs at about the same time, we still want
|
| - // to take the one that uses more memory.
|
| - if (abs((first.last_selected - second.last_selected).ToInternalValue()) <
|
| - kTimeBucketInterval)
|
| - return first.last_selected > second.last_selected;
|
| -
|
| - return first.memory_used < second.memory_used;
|
| + // Being more recently selected is more important.
|
| + return first.last_selected > second.last_selected;
|
| }
|
|
|
| -void OomPriorityManager::AdjustFocusedTabScore() {
|
| +void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
|
| ZygoteHost::GetInstance()->AdjustRendererOOMScore(
|
| focused_tab_pid_, chrome::kLowestRendererOomScore);
|
| @@ -138,11 +163,12 @@ void OomPriorityManager::AdjustFocusedTabScore() {
|
| void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
|
| BrowserThread::PostTask(
|
| BrowserThread::FILE, FROM_HERE,
|
| - NewRunnableMethod(this, &OomPriorityManager::AdjustFocusedTabScore));
|
| + NewRunnableMethod(
|
| + this, &OomPriorityManager::AdjustFocusedTabScoreOnFileThread));
|
| }
|
|
|
| void OomPriorityManager::Observe(int type, const NotificationSource& source,
|
| - const NotificationDetails& details) {
|
| + const NotificationDetails& details) {
|
| base::ProcessHandle handle = 0;
|
| base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
|
| switch (type) {
|
| @@ -193,71 +219,49 @@ void OomPriorityManager::Observe(int type, const NotificationSource& source,
|
| // 1) whether or not a tab is pinned
|
| // 2) last time a tab was selected
|
| // 3) is the tab currently selected
|
| -//
|
| -// We also need to collect:
|
| -// 4) size in memory of a tab
|
| -// But we do that in DoAdjustOomPriorities on the FILE thread so that
|
| -// we avoid jank, because it accesses /proc.
|
| void OomPriorityManager::AdjustOomPriorities() {
|
| if (BrowserList::size() == 0)
|
| return;
|
| + TabStatsList stats_list = GetTabStatsOnUIThread();
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &OomPriorityManager::AdjustOomPrioritiesOnFileThread,
|
| + stats_list));
|
| +}
|
|
|
| - {
|
| - base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
|
| - renderer_stats_.clear();
|
| - for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
|
| - browser_iterator != BrowserList::end(); ++browser_iterator) {
|
| - Browser* browser = *browser_iterator;
|
| - const TabStripModel* model = browser->tabstrip_model();
|
| - for (int i = 0; i < model->count(); i++) {
|
| - TabContents* contents = model->GetTabContentsAt(i)->tab_contents();
|
| - if (!contents->is_crashed()) {
|
| - RendererStats stats;
|
| - stats.last_selected = contents->last_selected_time();
|
| - stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
|
| - stats.is_pinned = model->IsTabPinned(i);
|
| - stats.memory_used = 0; // Calculated in DoAdjustOomPriorities.
|
| - stats.is_selected = model->IsTabSelected(i);
|
| - stats.title = contents->GetTitle();
|
| - renderer_stats_.push_back(stats);
|
| - }
|
| +OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + TabStatsList stats_list;
|
| + stats_list.reserve(32); // 99% of users have < 30 tabs open
|
| + for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
|
| + browser_iterator != BrowserList::end(); ++browser_iterator) {
|
| + Browser* browser = *browser_iterator;
|
| + const TabStripModel* model = browser->tabstrip_model();
|
| + for (int i = 0; i < model->count(); i++) {
|
| + TabContents* contents = model->GetTabContentsAt(i)->tab_contents();
|
| + if (!contents->is_crashed()) {
|
| + TabStats stats;
|
| + stats.last_selected = contents->last_selected_time();
|
| + stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
|
| + stats.is_pinned = model->IsTabPinned(i);
|
| + stats.is_selected = model->IsTabSelected(i);
|
| + stats.title = contents->GetTitle();
|
| + stats.tab_contents_id = IdFromTabContents(contents);
|
| + stats_list.push_back(stats);
|
| }
|
| }
|
| }
|
| -
|
| - BrowserThread::PostTask(
|
| - BrowserThread::FILE, FROM_HERE,
|
| - NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities));
|
| + // Sort the data we collected so that least desirable to be
|
| + // killed is first, most desirable is last.
|
| + std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);
|
| + return stats_list;
|
| }
|
|
|
| -void OomPriorityManager::DoAdjustOomPriorities() {
|
| +void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
|
| + TabStatsList stats_list) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| - base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
|
| base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
|
| - for (StatsList::iterator stats_iter = renderer_stats_.begin();
|
| - stats_iter != renderer_stats_.end(); ++stats_iter) {
|
| - scoped_ptr<ProcessMetrics> metrics(ProcessMetrics::CreateProcessMetrics(
|
| - stats_iter->renderer_handle));
|
| -
|
| - base::WorkingSetKBytes working_set_kbytes;
|
| - if (metrics->GetWorkingSetKBytes(&working_set_kbytes)) {
|
| - // We use the proportional set size (PSS) to calculate memory
|
| - // usage "badness" on Linux.
|
| - stats_iter->memory_used = working_set_kbytes.shared * 1024;
|
| - } else {
|
| - // and if for some reason we can't get PSS, we revert to using
|
| - // resident set size (RSS). This will be zero if the process
|
| - // has already gone away, but we can live with that, since the
|
| - // process is gone anyhow.
|
| - stats_iter->memory_used = metrics->GetWorkingSetSize();
|
| - }
|
| - }
|
| -
|
| - // Now we sort the data we collected so that least desirable to be
|
| - // killed is first, most desirable is last.
|
| - std::sort(renderer_stats_.begin(),
|
| - renderer_stats_.end(),
|
| - OomPriorityManager::CompareRendererStats);
|
|
|
| // Now we assign priorities based on the sorted list. We're
|
| // assigning priorities in the range of kLowestRendererOomScore to
|
| @@ -277,13 +281,13 @@ void OomPriorityManager::DoAdjustOomPriorities() {
|
| const int kPriorityRange = chrome::kHighestRendererOomScore -
|
| chrome::kLowestRendererOomScore;
|
| float priority_increment =
|
| - static_cast<float>(kPriorityRange) / renderer_stats_.size();
|
| + static_cast<float>(kPriorityRange) / stats_list.size();
|
| float priority = chrome::kLowestRendererOomScore;
|
| std::set<base::ProcessHandle> already_seen;
|
| int score = 0;
|
| ProcessScoreMap::iterator it;
|
| - for (StatsList::iterator iterator = renderer_stats_.begin();
|
| - iterator != renderer_stats_.end(); ++iterator) {
|
| + for (TabStatsList::iterator iterator = stats_list.begin();
|
| + iterator != stats_list.end(); ++iterator) {
|
| if (already_seen.find(iterator->renderer_handle) == already_seen.end()) {
|
| already_seen.insert(iterator->renderer_handle);
|
| // If a process has the same score as the newly calculated value,
|
|
|