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

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 #3 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
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 LOG(WARNING) << "Discarding tab " << idx << " id "
sky 2015/07/21 22:57:10 I see you're just moving code, but I'm surprised w
proberge 2015/07/22 14:21:06 I tried demoting it to LOG(INFO) but am getting a
sky 2015/07/22 15:29:41 You should use VLOG
proberge 2015/07/22 18:05:29 Thanks! Done.
181 << target_web_contents_id;
182 // Record statistics before discarding because we want to capture the
183 // memory state that lead to the discard.
184 RecordDiscardStatistics();
185 model->DiscardWebContentsAt(idx);
186 recent_tab_discard_ = true;
187 return true;
188 }
189 }
190 }
191 return false;
192 }
193
163 void OomPriorityManager::LogMemoryAndDiscardTab() { 194 void OomPriorityManager::LogMemoryAndDiscardTab() {
164 LogMemory("Tab Discards Memory details", 195 LogMemory("Tab Discards Memory details",
165 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab)); 196 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab));
166 } 197 }
167 198
168 void OomPriorityManager::LogMemory(const std::string& title, 199 void OomPriorityManager::LogMemory(const std::string& title,
169 const base::Closure& callback) { 200 const base::Closure& callback) {
170 DCHECK_CURRENTLY_ON(BrowserThread::UI); 201 DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 OomMemoryDetails::Log(title, callback); 202 OomMemoryDetails::Log(title, callback);
172 } 203 }
(...skipping 23 matching lines...) Expand all
196 // Prefix-match against the table above. Use strncmp to avoid allocating 227 // Prefix-match against the table above. Use strncmp to avoid allocating
197 // memory to convert the URL prefix constants into std::strings. 228 // memory to convert the URL prefix constants into std::strings.
198 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) { 229 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) {
199 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i], 230 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i],
200 strlen(kInternalPagePrefixes[i]))) 231 strlen(kInternalPagePrefixes[i])))
201 return true; 232 return true;
202 } 233 }
203 return false; 234 return false;
204 } 235 }
205 236
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() { 237 void OomPriorityManager::RecordDiscardStatistics() {
232 // Record a raw count so we can compare to discard reloads. 238 // Record a raw count so we can compare to discard reloads.
233 discard_count_++; 239 discard_count_++;
234 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1, 240 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1,
235 1000, 50); 241 1000, 50);
236 242
237 // TODO(jamescook): Maybe incorporate extension count? 243 // TODO(jamescook): Maybe incorporate extension count?
238 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100, 244 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100,
239 50); 245 50);
240 #if defined(OS_CHROMEOS) 246 #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 361 // We were probably suspended, move our event timers forward in time so
356 // when we subtract them out later we are counting "uptime". 362 // when we subtract them out later we are counting "uptime".
357 start_time_ += suspend_time; 363 start_time_ += suspend_time;
358 if (!last_discard_time_.is_null()) 364 if (!last_discard_time_.is_null())
359 last_discard_time_ += suspend_time; 365 last_discard_time_ += suspend_time;
360 } 366 }
361 } 367 }
362 last_adjust_time_ = TimeTicks::Now(); 368 last_adjust_time_ = TimeTicks::Now();
363 369
364 #if defined(OS_CHROMEOS) 370 #if defined(OS_CHROMEOS)
365 TabStatsList stats_list = GetTabStatsOnUIThread(); 371 TabStatsList stats_list = GetTabStats();
366 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. 372 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj.
367 delegate_->AdjustOomPriorities(stats_list); 373 delegate_->AdjustOomPriorities(stats_list);
368 #endif 374 #endif
369 } 375 }
370 376
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, 377 void OomPriorityManager::AddTabStats(BrowserList* browser_list,
402 bool active_desktop, 378 bool active_desktop,
403 TabStatsList* stats_list) { 379 TabStatsList* stats_list) {
404 // If it's the active desktop, the first window will be the active one. 380 // If it's the active desktop, the first window will be the active one.
405 // Otherwise, we assume no active windows. 381 // Otherwise, we assume no active windows.
406 bool browser_active = active_desktop; 382 bool browser_active = active_desktop;
407 for (BrowserList::const_reverse_iterator browser_iterator = 383 for (BrowserList::const_reverse_iterator browser_iterator =
408 browser_list->begin_last_active(); 384 browser_list->begin_last_active();
409 browser_iterator != browser_list->end_last_active(); 385 browser_iterator != browser_list->end_last_active();
410 ++browser_iterator) { 386 ++browser_iterator) {
411 Browser* browser = *browser_iterator; 387 Browser* browser = *browser_iterator;
412 bool is_browser_for_app = browser->is_app(); 388 bool is_browser_for_app = browser->is_app();
413 const TabStripModel* model = browser->tab_strip_model(); 389 const TabStripModel* model = browser->tab_strip_model();
414 for (int i = 0; i < model->count(); i++) { 390 for (int i = 0; i < model->count(); i++) {
415 WebContents* contents = model->GetWebContentsAt(i); 391 WebContents* contents = model->GetWebContentsAt(i);
416 if (!contents->IsCrashed()) { 392 if (!contents->IsCrashed()) {
417 TabStats stats; 393 TabStats stats;
418 stats.is_app = is_browser_for_app; 394 stats.is_app = is_browser_for_app;
419 stats.is_internal_page = 395 stats.is_internal_page =
420 IsInternalPage(contents->GetLastCommittedURL()); 396 IsInternalPage(contents->GetLastCommittedURL());
421 stats.is_playing_audio = chrome::IsPlayingAudio(contents); 397 stats.is_playing_audio = chrome::IsPlayingAudio(contents);
422 stats.is_pinned = model->IsTabPinned(i); 398 stats.is_pinned = model->IsTabPinned(i);
423 stats.is_selected = browser_active && model->IsTabSelected(i); 399 stats.is_selected = browser_active && model->IsTabSelected(i);
424 stats.is_discarded = model->IsTabDiscarded(i); 400 stats.is_discarded = model->IsTabDiscarded(i);
425 stats.last_active = contents->GetLastActiveTime(); 401 stats.last_active = contents->GetLastActiveTime();
426 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); 402 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
427 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); 403 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID();
404 #if defined(OS_CHROMEOS)
405 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id);
406 #endif
428 stats.title = contents->GetTitle(); 407 stats.title = contents->GetTitle();
429 stats.tab_contents_id = IdFromWebContents(contents); 408 stats.tab_contents_id = IdFromWebContents(contents);
430 stats_list->push_back(stats); 409 stats_list->push_back(stats);
431 } 410 }
432 } 411 }
433 // We process the active browser window in the first iteration. 412 // We process the active browser window in the first iteration.
434 browser_active = false; 413 browser_active = false;
435 } 414 }
436 } 415 }
437 416
438 void OomPriorityManager::OnMemoryPressure( 417 void OomPriorityManager::OnMemoryPressure(
439 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { 418 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
440 // For the moment we only do something when we reach a critical state. 419 // For the moment we only do something when we reach a critical state.
441 if (memory_pressure_level == 420 if (memory_pressure_level ==
442 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { 421 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
443 LogMemoryAndDiscardTab(); 422 LogMemoryAndDiscardTab();
444 } 423 }
445 // TODO(skuhne): If more memory pressure levels are introduced, we might 424 // TODO(skuhne): If more memory pressure levels are introduced, we might
446 // consider to call PurgeBrowserMemory() before CRITICAL is reached. 425 // consider to call PurgeBrowserMemory() before CRITICAL is reached.
447 } 426 }
448 427
449 } // namespace memory 428 } // namespace memory
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698