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

Side by Side Diff: chrome/browser/memory/oom_priority_manager.cc

Issue 1249693002: Improve the about/discards UI, allowing each tab to be individually closed. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Resolve comments on #6 Created 5 years, 5 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
« no previous file with comments | « chrome/browser/memory/oom_priority_manager.h ('k') | chrome/browser/memory/tab_stats.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/memory/oom_priority_manager.h" 5 #include "chrome/browser/memory/oom_priority_manager.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 #include <vector> 9 #include <vector>
10 10
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 } 111 }
112 } 112 }
113 } 113 }
114 114
115 void OomPriorityManager::Stop() { 115 void OomPriorityManager::Stop() {
116 update_timer_.Stop(); 116 update_timer_.Stop();
117 recent_tab_discard_timer_.Stop(); 117 recent_tab_discard_timer_.Stop();
118 memory_pressure_listener_.reset(); 118 memory_pressure_listener_.reset();
119 } 119 }
120 120
121 std::vector<base::string16> OomPriorityManager::GetTabTitles() { 121 // Things we need to collect on the browser thread (because TabStripModel isn't
122 TabStatsList stats = GetTabStatsOnUIThread(); 122 // thread safe):
123 std::vector<base::string16> titles; 123 // 1) whether or not a tab is pinned
124 titles.reserve(stats.size()); 124 // 2) last time a tab was selected
125 TabStatsList::iterator it = stats.begin(); 125 // 3) is the tab currently selected
126 for (; it != stats.end(); ++it) { 126 TabStatsList OomPriorityManager::GetTabStats() {
127 base::string16 str; 127 DCHECK_CURRENTLY_ON(BrowserThread::UI);
128 str.reserve(4096); 128 TabStatsList stats_list;
129 #if defined(OS_CHROMEOS) 129 stats_list.reserve(32); // 99% of users have < 30 tabs open
130 int score = delegate_->GetOomScore(it->child_process_host_id); 130
131 str += base::IntToString16(score); 131 // We go through each window to get all the tabs. Depending on the platform,
132 str += base::ASCIIToUTF16(" - "); 132 // windows are either native or ash or both. We want to make sure to go
133 #endif 133 // through them all, starting with the active window first (we use
134 str += it->title; 134 // chrome::GetActiveDesktop to get the current used type).
135 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); 135 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true,
136 str += base::ASCIIToUTF16(it->is_internal_page ? " internal_page" : ""); 136 &stats_list);
137 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); 137 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) {
138 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); 138 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE),
139 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); 139 false, &stats_list);
140 titles.push_back(str); 140 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) {
141 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false,
142 &stats_list);
141 } 143 }
142 return titles; 144
145 // Sort the data we collected so that least desirable to be
146 // killed is first, most desirable is last.
147 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);
148 return stats_list;
143 } 149 }
144 150
145 // TODO(jamescook): This should consider tabs with references to other tabs, 151 // TODO(jamescook): This should consider tabs with references to other tabs,
146 // such as tabs created with JavaScript window.open(). We might want to 152 // such as tabs created with JavaScript window.open(). We might want to
147 // discard the entire set together, or use that in the priority computation. 153 // discard the entire set together, or use that in the priority computation.
148 bool OomPriorityManager::DiscardTab() { 154 bool OomPriorityManager::DiscardTab() {
149 DCHECK_CURRENTLY_ON(BrowserThread::UI); 155 DCHECK_CURRENTLY_ON(BrowserThread::UI);
150 TabStatsList stats = GetTabStatsOnUIThread(); 156 TabStatsList stats = GetTabStats();
151 if (stats.empty()) 157 if (stats.empty())
152 return false; 158 return false;
153 // Loop until we find a non-discarded tab to kill. 159 // Loop until we find a non-discarded tab to kill.
154 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); 160 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin();
155 stats_rit != stats.rend(); ++stats_rit) { 161 stats_rit != stats.rend(); ++stats_rit) {
156 int64 least_important_tab_id = stats_rit->tab_contents_id; 162 int64 least_important_tab_id = stats_rit->tab_contents_id;
157 if (DiscardTabById(least_important_tab_id)) 163 if (DiscardTabById(least_important_tab_id))
158 return true; 164 return true;
159 } 165 }
160 return false; 166 return false;
161 } 167 }
162 168
169 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) {
170 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
171 Browser* browser = *it;
172 TabStripModel* model = browser->tab_strip_model();
173 for (int idx = 0; idx < model->count(); idx++) {
174 // Can't discard tabs that are already discarded or active.
175 if (model->IsTabDiscarded(idx) || (model->active_index() == idx))
176 continue;
177 WebContents* web_contents = model->GetWebContentsAt(idx);
178 int64 web_contents_id = IdFromWebContents(web_contents);
179 if (web_contents_id == target_web_contents_id) {
180 VLOG(1) << "Discarding tab " << idx << " id " << target_web_contents_id;
181 // Record statistics before discarding because we want to capture the
182 // memory state that lead to the discard.
183 RecordDiscardStatistics();
184 model->DiscardWebContentsAt(idx);
185 recent_tab_discard_ = true;
186 return true;
187 }
188 }
189 }
190 return false;
191 }
192
163 void OomPriorityManager::LogMemoryAndDiscardTab() { 193 void OomPriorityManager::LogMemoryAndDiscardTab() {
164 LogMemory("Tab Discards Memory details", 194 LogMemory("Tab Discards Memory details",
165 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab)); 195 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab));
166 } 196 }
167 197
168 void OomPriorityManager::LogMemory(const std::string& title, 198 void OomPriorityManager::LogMemory(const std::string& title,
169 const base::Closure& callback) { 199 const base::Closure& callback) {
170 DCHECK_CURRENTLY_ON(BrowserThread::UI); 200 DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 OomMemoryDetails::Log(title, callback); 201 OomMemoryDetails::Log(title, callback);
172 } 202 }
(...skipping 23 matching lines...) Expand all
196 // Prefix-match against the table above. Use strncmp to avoid allocating 226 // Prefix-match against the table above. Use strncmp to avoid allocating
197 // memory to convert the URL prefix constants into std::strings. 227 // memory to convert the URL prefix constants into std::strings.
198 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) { 228 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) {
199 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i], 229 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i],
200 strlen(kInternalPagePrefixes[i]))) 230 strlen(kInternalPagePrefixes[i])))
201 return true; 231 return true;
202 } 232 }
203 return false; 233 return false;
204 } 234 }
205 235
206 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) {
207 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
208 Browser* browser = *it;
209 TabStripModel* model = browser->tab_strip_model();
210 for (int idx = 0; idx < model->count(); idx++) {
211 // Can't discard tabs that are already discarded or active.
212 if (model->IsTabDiscarded(idx) || (model->active_index() == idx))
213 continue;
214 WebContents* web_contents = model->GetWebContentsAt(idx);
215 int64 web_contents_id = IdFromWebContents(web_contents);
216 if (web_contents_id == target_web_contents_id) {
217 LOG(WARNING) << "Discarding tab " << idx << " id "
218 << target_web_contents_id;
219 // Record statistics before discarding because we want to capture the
220 // memory state that lead to the discard.
221 RecordDiscardStatistics();
222 model->DiscardWebContentsAt(idx);
223 recent_tab_discard_ = true;
224 return true;
225 }
226 }
227 }
228 return false;
229 }
230
231 void OomPriorityManager::RecordDiscardStatistics() { 236 void OomPriorityManager::RecordDiscardStatistics() {
232 // Record a raw count so we can compare to discard reloads. 237 // Record a raw count so we can compare to discard reloads.
233 discard_count_++; 238 discard_count_++;
234 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1, 239 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1,
235 1000, 50); 240 1000, 50);
236 241
237 // TODO(jamescook): Maybe incorporate extension count? 242 // TODO(jamescook): Maybe incorporate extension count?
238 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100, 243 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100,
239 50); 244 50);
240 #if defined(OS_CHROMEOS) 245 #if defined(OS_CHROMEOS)
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 // We were probably suspended, move our event timers forward in time so 360 // We were probably suspended, move our event timers forward in time so
356 // when we subtract them out later we are counting "uptime". 361 // when we subtract them out later we are counting "uptime".
357 start_time_ += suspend_time; 362 start_time_ += suspend_time;
358 if (!last_discard_time_.is_null()) 363 if (!last_discard_time_.is_null())
359 last_discard_time_ += suspend_time; 364 last_discard_time_ += suspend_time;
360 } 365 }
361 } 366 }
362 last_adjust_time_ = TimeTicks::Now(); 367 last_adjust_time_ = TimeTicks::Now();
363 368
364 #if defined(OS_CHROMEOS) 369 #if defined(OS_CHROMEOS)
365 TabStatsList stats_list = GetTabStatsOnUIThread(); 370 TabStatsList stats_list = GetTabStats();
366 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. 371 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj.
367 delegate_->AdjustOomPriorities(stats_list); 372 delegate_->AdjustOomPriorities(stats_list);
368 #endif 373 #endif
369 } 374 }
370 375
371 // Things we need to collect on the browser thread (because TabStripModel isn't
372 // thread safe):
373 // 1) whether or not a tab is pinned
374 // 2) last time a tab was selected
375 // 3) is the tab currently selected
376 TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
377 DCHECK_CURRENTLY_ON(BrowserThread::UI);
378 TabStatsList stats_list;
379 stats_list.reserve(32); // 99% of users have < 30 tabs open
380
381 // We go through each window to get all the tabs. Depending on the platform,
382 // windows are either native or ash or both. We want to make sure to go
383 // through them all, starting with the active window first (we use
384 // chrome::GetActiveDesktop to get the current used type).
385 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true,
386 &stats_list);
387 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) {
388 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE),
389 false, &stats_list);
390 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) {
391 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false,
392 &stats_list);
393 }
394
395 // Sort the data we collected so that least desirable to be
396 // killed is first, most desirable is last.
397 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);
398 return stats_list;
399 }
400
401 void OomPriorityManager::AddTabStats(BrowserList* browser_list, 376 void OomPriorityManager::AddTabStats(BrowserList* browser_list,
402 bool active_desktop, 377 bool active_desktop,
403 TabStatsList* stats_list) { 378 TabStatsList* stats_list) {
404 // If it's the active desktop, the first window will be the active one. 379 // If it's the active desktop, the first window will be the active one.
405 // Otherwise, we assume no active windows. 380 // Otherwise, we assume no active windows.
406 bool browser_active = active_desktop; 381 bool browser_active = active_desktop;
407 for (BrowserList::const_reverse_iterator browser_iterator = 382 for (BrowserList::const_reverse_iterator browser_iterator =
408 browser_list->begin_last_active(); 383 browser_list->begin_last_active();
409 browser_iterator != browser_list->end_last_active(); 384 browser_iterator != browser_list->end_last_active();
410 ++browser_iterator) { 385 ++browser_iterator) {
411 Browser* browser = *browser_iterator; 386 Browser* browser = *browser_iterator;
412 bool is_browser_for_app = browser->is_app(); 387 bool is_browser_for_app = browser->is_app();
413 const TabStripModel* model = browser->tab_strip_model(); 388 const TabStripModel* model = browser->tab_strip_model();
414 for (int i = 0; i < model->count(); i++) { 389 for (int i = 0; i < model->count(); i++) {
415 WebContents* contents = model->GetWebContentsAt(i); 390 WebContents* contents = model->GetWebContentsAt(i);
416 if (!contents->IsCrashed()) { 391 if (!contents->IsCrashed()) {
417 TabStats stats; 392 TabStats stats;
418 stats.is_app = is_browser_for_app; 393 stats.is_app = is_browser_for_app;
419 stats.is_internal_page = 394 stats.is_internal_page =
420 IsInternalPage(contents->GetLastCommittedURL()); 395 IsInternalPage(contents->GetLastCommittedURL());
421 stats.is_playing_audio = chrome::IsPlayingAudio(contents); 396 stats.is_playing_audio = chrome::IsPlayingAudio(contents);
422 stats.is_pinned = model->IsTabPinned(i); 397 stats.is_pinned = model->IsTabPinned(i);
423 stats.is_selected = browser_active && model->IsTabSelected(i); 398 stats.is_selected = browser_active && model->IsTabSelected(i);
424 stats.is_discarded = model->IsTabDiscarded(i); 399 stats.is_discarded = model->IsTabDiscarded(i);
425 stats.last_active = contents->GetLastActiveTime(); 400 stats.last_active = contents->GetLastActiveTime();
426 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); 401 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
427 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); 402 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID();
403 #if defined(OS_CHROMEOS)
404 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id);
405 #endif
428 stats.title = contents->GetTitle(); 406 stats.title = contents->GetTitle();
429 stats.tab_contents_id = IdFromWebContents(contents); 407 stats.tab_contents_id = IdFromWebContents(contents);
430 stats_list->push_back(stats); 408 stats_list->push_back(stats);
431 } 409 }
432 } 410 }
433 // We process the active browser window in the first iteration. 411 // We process the active browser window in the first iteration.
434 browser_active = false; 412 browser_active = false;
435 } 413 }
436 } 414 }
437 415
438 void OomPriorityManager::OnMemoryPressure( 416 void OomPriorityManager::OnMemoryPressure(
439 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { 417 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
440 // For the moment we only do something when we reach a critical state. 418 // For the moment we only do something when we reach a critical state.
441 if (memory_pressure_level == 419 if (memory_pressure_level ==
442 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { 420 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
443 LogMemoryAndDiscardTab(); 421 LogMemoryAndDiscardTab();
444 } 422 }
445 // TODO(skuhne): If more memory pressure levels are introduced, we might 423 // TODO(skuhne): If more memory pressure levels are introduced, we might
446 // consider to call PurgeBrowserMemory() before CRITICAL is reached. 424 // consider to call PurgeBrowserMemory() before CRITICAL is reached.
447 } 425 }
448 426
449 } // namespace memory 427 } // namespace memory
OLDNEW
« no previous file with comments | « chrome/browser/memory/oom_priority_manager.h ('k') | chrome/browser/memory/tab_stats.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698