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
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
11 #include "ash/multi_profile_uma.h"
12 #include "ash/session_state_delegate.h"
13 #include "ash/shell.h"
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/command_line.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram.h"
19 #include "base/process/process.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/synchronization/lock.h"
25 #include "base/threading/thread.h"
26 #include "base/time/time.h"
27 #include "build/build_config.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/browser_process_platform_part_chromeos.h"
30 #include "chrome/browser/memory_details.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_iterator.h"
33 #include "chrome/browser/ui/browser_list.h"
34 #include "chrome/browser/ui/host_desktop.h"
35 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
36 #include "chrome/browser/ui/tabs/tab_strip_model.h"
37 #include "chrome/browser/ui/tabs/tab_utils.h"
38 #include "chrome/common/chrome_constants.h"
39 #include "chrome/common/url_constants.h"
40 #include "chromeos/chromeos_switches.h"
41 #include "chromeos/memory/low_memory_listener.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/notification_types.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/render_widget_host.h"
47 #include "content/public/browser/web_contents.h"
48 #include "content/public/browser/zygote_host_linux.h"
49 #include "ui/base/text/bytes_formatting.h"
51 using base::TimeDelta
;
52 using base::TimeTicks
;
53 using content::BrowserThread
;
54 using content::WebContents
;
60 // Record a size in megabytes, over a potential interval up to 32 GB.
61 #define HISTOGRAM_MEGABYTES(name, sample) \
62 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 32768, 50)
64 // The default interval in seconds after which to adjust the oom_score_adj
66 const int kAdjustmentIntervalSeconds
= 10;
68 // For each period of this length we record a statistic to indicate whether
69 // or not the user experienced a low memory event. If you change this interval
70 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic.
71 const int kRecentTabDiscardIntervalSeconds
= 60;
73 // If there has been no priority adjustment in this interval, we assume the
74 // machine was suspended and correct our timing statistics.
75 const int kSuspendThresholdSeconds
= kAdjustmentIntervalSeconds
* 4;
77 // When switching to a new tab the tab's renderer's OOM score needs to be
78 // updated to reflect its front-most status and protect it from discard.
79 // However, doing this immediately might slow down tab switch time, so wait
80 // a little while before doing the adjustment.
81 const int kFocusedTabScoreAdjustIntervalMs
= 500;
83 // Returns a unique ID for a WebContents. Do not cast back to a pointer, as
84 // the WebContents could be deleted if the user closed the tab.
85 int64
IdFromWebContents(WebContents
* web_contents
) {
86 return reinterpret_cast<int64
>(web_contents
);
89 // Records a statistics |sample| for UMA histogram |name| using a linear
90 // distribution of buckets.
91 void RecordLinearHistogram(const std::string
& name
,
94 size_t bucket_count
) {
95 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram
96 // instance and thus only work if |name| is constant.
97 base::HistogramBase
* counter
= base::LinearHistogram::FactoryGet(
99 1, // Minimum. The 0 bin for underflow is automatically added.
100 maximum
+ 1, // Ensure bucket size of |maximum| / |bucket_count|.
101 bucket_count
+ 2, // Account for the underflow and overflow bins.
102 base::Histogram::kUmaTargetedHistogramFlag
);
103 counter
->Add(sample
);
108 ////////////////////////////////////////////////////////////////////////////////
109 // OomMemoryDetails logs details about all Chrome processes during an out-of-
110 // memory event in an attempt to identify the culprit, then discards a tab and
112 class OomMemoryDetails
: public MemoryDetails
{
116 // MemoryDetails overrides:
117 virtual void OnDetailsAvailable() OVERRIDE
;
120 virtual ~OomMemoryDetails() {}
122 TimeTicks start_time_
;
124 DISALLOW_COPY_AND_ASSIGN(OomMemoryDetails
);
127 OomMemoryDetails::OomMemoryDetails() {
128 AddRef(); // Released in OnDetailsAvailable().
129 start_time_
= TimeTicks::Now();
132 void OomMemoryDetails::OnDetailsAvailable() {
133 TimeDelta delta
= TimeTicks::Now() - start_time_
;
134 // These logs are collected by user feedback reports. We want them to help
135 // diagnose user-reported problems with frequently discarded tabs.
136 std::string log_string
= ToLogString();
137 base::SystemMemoryInfoKB memory
;
138 if (base::GetSystemMemoryInfo(&memory
) && memory
.gem_size
!= -1) {
139 log_string
+= "Graphics ";
140 log_string
+= UTF16ToASCII(ui::FormatBytes(memory
.gem_size
));
142 LOG(WARNING
) << "OOM details (" << delta
.InMilliseconds() << " ms):\n"
144 if (g_browser_process
&&
145 g_browser_process
->platform_part()->oom_priority_manager()) {
146 OomPriorityManager
* manager
=
147 g_browser_process
->platform_part()->oom_priority_manager();
148 manager
->PurgeBrowserMemory();
149 manager
->DiscardTab();
151 // Delete ourselves so we don't have to worry about OomPriorityManager
152 // deleting us when we're still working.
156 ////////////////////////////////////////////////////////////////////////////////
157 // OomPriorityManager
159 OomPriorityManager::TabStats::TabStats()
161 is_reloadable_ui(false),
162 is_playing_audio(false),
170 OomPriorityManager::TabStats::~TabStats() {
173 OomPriorityManager::OomPriorityManager()
174 : focused_tab_pid_(0),
175 low_memory_listener_(new LowMemoryListener(this)),
177 recent_tab_discard_(false) {
179 content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
180 content::NotificationService::AllBrowserContextsAndSources());
182 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
183 content::NotificationService::AllBrowserContextsAndSources());
185 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
186 content::NotificationService::AllBrowserContextsAndSources());
189 OomPriorityManager::~OomPriorityManager() {
193 void OomPriorityManager::Start() {
194 if (!timer_
.IsRunning()) {
195 timer_
.Start(FROM_HERE
,
196 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds
),
198 &OomPriorityManager::AdjustOomPriorities
);
200 if (!recent_tab_discard_timer_
.IsRunning()) {
201 recent_tab_discard_timer_
.Start(
203 TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds
),
205 &OomPriorityManager::RecordRecentTabDiscard
);
207 if (low_memory_listener_
.get())
208 low_memory_listener_
->Start();
209 start_time_
= TimeTicks::Now();
212 void OomPriorityManager::Stop() {
214 recent_tab_discard_timer_
.Stop();
215 if (low_memory_listener_
.get())
216 low_memory_listener_
->Stop();
219 std::vector
<base::string16
> OomPriorityManager::GetTabTitles() {
220 TabStatsList stats
= GetTabStatsOnUIThread();
221 base::AutoLock
pid_to_oom_score_autolock(pid_to_oom_score_lock_
);
222 std::vector
<base::string16
> titles
;
223 titles
.reserve(stats
.size());
224 TabStatsList::iterator it
= stats
.begin();
225 for ( ; it
!= stats
.end(); ++it
) {
228 int score
= pid_to_oom_score_
[it
->renderer_handle
];
229 str
+= base::IntToString16(score
);
230 str
+= base::ASCIIToUTF16(" - ");
232 str
+= base::ASCIIToUTF16(it
->is_app
? " app" : "");
233 str
+= base::ASCIIToUTF16(it
->is_reloadable_ui
? " reloadable_ui" : "");
234 str
+= base::ASCIIToUTF16(it
->is_playing_audio
? " playing_audio" : "");
235 str
+= base::ASCIIToUTF16(it
->is_pinned
? " pinned" : "");
236 str
+= base::ASCIIToUTF16(it
->is_discarded
? " discarded" : "");
237 titles
.push_back(str
);
242 // TODO(jamescook): This should consider tabs with references to other tabs,
243 // such as tabs created with JavaScript window.open(). We might want to
244 // discard the entire set together, or use that in the priority computation.
245 bool OomPriorityManager::DiscardTab() {
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
247 TabStatsList stats
= GetTabStatsOnUIThread();
250 // Loop until we find a non-discarded tab to kill.
251 for (TabStatsList::const_reverse_iterator stats_rit
= stats
.rbegin();
252 stats_rit
!= stats
.rend();
254 int64 least_important_tab_id
= stats_rit
->tab_contents_id
;
255 if (DiscardTabById(least_important_tab_id
))
261 void OomPriorityManager::LogMemoryAndDiscardTab() {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
263 // Deletes itself upon completion.
264 OomMemoryDetails
* details
= new OomMemoryDetails();
265 details
->StartFetch(MemoryDetails::SKIP_USER_METRICS
);
268 ///////////////////////////////////////////////////////////////////////////////
269 // OomPriorityManager, private:
272 bool OomPriorityManager::IsReloadableUI(const GURL
& url
) {
273 // There are many chrome:// UI URLs, but only look for the ones that users
274 // are likely to have open. Most of the benefit is the from NTP URL.
275 const char* kReloadableUrlPrefixes
[] = {
276 chrome::kChromeUIDownloadsURL
,
277 chrome::kChromeUIHistoryURL
,
278 chrome::kChromeUINewTabURL
,
279 chrome::kChromeUISettingsURL
,
281 // Prefix-match against the table above. Use strncmp to avoid allocating
282 // memory to convert the URL prefix constants into std::strings.
283 for (size_t i
= 0; i
< arraysize(kReloadableUrlPrefixes
); ++i
) {
284 if (!strncmp(url
.spec().c_str(),
285 kReloadableUrlPrefixes
[i
],
286 strlen(kReloadableUrlPrefixes
[i
])))
292 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id
) {
293 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
294 Browser
* browser
= *it
;
295 TabStripModel
* model
= browser
->tab_strip_model();
296 for (int idx
= 0; idx
< model
->count(); idx
++) {
297 // Can't discard tabs that are already discarded or active.
298 if (model
->IsTabDiscarded(idx
) || (model
->active_index() == idx
))
300 WebContents
* web_contents
= model
->GetWebContentsAt(idx
);
301 int64 web_contents_id
= IdFromWebContents(web_contents
);
302 if (web_contents_id
== target_web_contents_id
) {
303 LOG(WARNING
) << "Discarding tab " << idx
304 << " id " << target_web_contents_id
;
305 // Record statistics before discarding because we want to capture the
306 // memory state that lead to the discard.
307 RecordDiscardStatistics();
308 model
->DiscardWebContentsAt(idx
);
309 recent_tab_discard_
= true;
317 void OomPriorityManager::RecordDiscardStatistics() {
318 // Record a raw count so we can compare to discard reloads.
320 UMA_HISTOGRAM_CUSTOM_COUNTS(
321 "Tabs.Discard.DiscardCount", discard_count_
, 1, 1000, 50);
323 // TODO(jamescook): Maybe incorporate extension count?
324 UMA_HISTOGRAM_CUSTOM_COUNTS(
325 "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50);
327 // Record the discarded tab in relation to the amount of simultaneously
329 ash::MultiProfileUMA::RecordDiscardedTab(
330 ash::Shell::GetInstance()->session_state_delegate()->
331 NumberOfLoggedInUsers());
333 // TODO(jamescook): If the time stats prove too noisy, then divide up users
334 // based on how heavily they use Chrome using tab count as a proxy.
335 // Bin into <= 1, <= 2, <= 4, <= 8, etc.
336 if (last_discard_time_
.is_null()) {
337 // This is the first discard this session.
338 TimeDelta interval
= TimeTicks::Now() - start_time_
;
339 int interval_seconds
= static_cast<int>(interval
.InSeconds());
340 // Record time in seconds over an interval of approximately 1 day.
341 UMA_HISTOGRAM_CUSTOM_COUNTS(
342 "Tabs.Discard.InitialTime2", interval_seconds
, 1, 100000, 50);
344 // Not the first discard, so compute time since last discard.
345 TimeDelta interval
= TimeTicks::Now() - last_discard_time_
;
346 int interval_ms
= static_cast<int>(interval
.InMilliseconds());
347 // Record time in milliseconds over an interval of approximately 1 day.
348 // Start at 100 ms to get extra resolution in the target 750 ms range.
349 UMA_HISTOGRAM_CUSTOM_COUNTS(
350 "Tabs.Discard.IntervalTime2", interval_ms
, 100, 100000 * 1000, 50);
352 // Record Chrome's concept of system memory usage at the time of the discard.
353 base::SystemMemoryInfoKB memory
;
354 if (base::GetSystemMemoryInfo(&memory
)) {
355 // TODO(jamescook): Remove this after R25 is deployed to stable. It does
356 // not have sufficient resolution in the 2-4 GB range and does not properly
357 // account for graphics memory on ARM. Replace with MemAllocatedMB below.
358 int mem_anonymous_mb
= (memory
.active_anon
+ memory
.inactive_anon
) / 1024;
359 HISTOGRAM_MEGABYTES("Tabs.Discard.MemAnonymousMB", mem_anonymous_mb
);
361 // Record graphics GEM object size in a histogram with 50 MB buckets.
362 int mem_graphics_gem_mb
= 0;
363 if (memory
.gem_size
!= -1)
364 mem_graphics_gem_mb
= memory
.gem_size
/ 1024 / 1024;
365 RecordLinearHistogram(
366 "Tabs.Discard.MemGraphicsMB", mem_graphics_gem_mb
, 2500, 50);
368 // Record shared memory (used by renderer/GPU buffers).
369 int mem_shmem_mb
= memory
.shmem
/ 1024;
370 RecordLinearHistogram("Tabs.Discard.MemShmemMB", mem_shmem_mb
, 2500, 50);
372 // On Intel, graphics objects are in anonymous pages, but on ARM they are
373 // not. For a total "allocated count" add in graphics pages on ARM.
374 int mem_allocated_mb
= mem_anonymous_mb
;
375 #if defined(ARCH_CPU_ARM_FAMILY)
376 mem_allocated_mb
+= mem_graphics_gem_mb
;
378 UMA_HISTOGRAM_CUSTOM_COUNTS(
379 "Tabs.Discard.MemAllocatedMB", mem_allocated_mb
, 256, 32768, 50);
381 int mem_available_mb
=
382 (memory
.active_file
+ memory
.inactive_file
+ memory
.free
) / 1024;
383 HISTOGRAM_MEGABYTES("Tabs.Discard.MemAvailableMB", mem_available_mb
);
385 // Set up to record the next interval.
386 last_discard_time_
= TimeTicks::Now();
389 void OomPriorityManager::RecordRecentTabDiscard() {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
391 // If we change the interval we need to change the histogram name.
392 UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute",
393 recent_tab_discard_
);
394 // Reset for the next interval.
395 recent_tab_discard_
= false;
398 void OomPriorityManager::PurgeBrowserMemory() {
399 // Based on experimental evidence, attempts to free memory from renderers
400 // have been too slow to use in OOM situations (V8 garbage collection) or
401 // do not lead to persistent decreased usage (image/bitmap caches). This
402 // function therefore only targets large blocks of memory in the browser.
403 for (TabContentsIterator it
; !it
.done(); it
.Next()) {
404 WebContents
* web_contents
= *it
;
405 // Screenshots can consume ~5 MB per web contents for platforms that do
406 // touch back/forward.
407 web_contents
->GetController().ClearAllScreenshots();
409 // TODO(jamescook): Are there other things we could flush? Drive metadata?
412 int OomPriorityManager::GetTabCount() const {
414 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
415 tab_count
+= it
->tab_strip_model()->count();
419 // Returns true if |first| is considered less desirable to be killed
421 bool OomPriorityManager::CompareTabStats(TabStats first
,
423 // Being currently selected is most important to protect.
424 if (first
.is_selected
!= second
.is_selected
)
425 return first
.is_selected
;
427 // Tab with internal web UI like NTP or Settings are good choices to discard,
428 // so protect non-Web UI and let the other conditionals finish the sort.
429 if (first
.is_reloadable_ui
!= second
.is_reloadable_ui
)
430 return !first
.is_reloadable_ui
;
432 // Being pinned is important to protect.
433 if (first
.is_pinned
!= second
.is_pinned
)
434 return first
.is_pinned
;
436 // Being an app is important too, as you're the only visible surface in the
437 // window and we don't want to discard that.
438 if (first
.is_app
!= second
.is_app
)
441 // Protect streaming audio and video conferencing tabs.
442 if (first
.is_playing_audio
!= second
.is_playing_audio
)
443 return first
.is_playing_audio
;
445 // TODO(jamescook): Incorporate sudden_termination_allowed into the sort
446 // order. We don't do this now because pages with unload handlers set
447 // sudden_termination_allowed false, and that covers too many common pages
448 // with ad networks and statistics scripts. Ideally we would like to check
449 // for beforeUnload handlers, which are likely to present a dialog asking
450 // if the user wants to discard state. crbug.com/123049
452 // Being more recently selected is more important.
453 return first
.last_selected
> second
.last_selected
;
456 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
458 base::AutoLock
pid_to_oom_score_autolock(pid_to_oom_score_lock_
);
459 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
460 focused_tab_pid_
, chrome::kLowestRendererOomScore
);
461 pid_to_oom_score_
[focused_tab_pid_
] = chrome::kLowestRendererOomScore
;
464 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
465 BrowserThread::PostTask(
466 BrowserThread::FILE, FROM_HERE
,
467 base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread
,
468 base::Unretained(this)));
471 void OomPriorityManager::Observe(int type
,
472 const content::NotificationSource
& source
,
473 const content::NotificationDetails
& details
) {
474 base::ProcessHandle handle
= 0;
475 base::AutoLock
pid_to_oom_score_autolock(pid_to_oom_score_lock_
);
477 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
: {
479 content::Details
<content::RenderProcessHost::RendererClosedDetails
>(
481 pid_to_oom_score_
.erase(handle
);
484 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
: {
485 handle
= content::Source
<content::RenderProcessHost
>(source
)->
487 pid_to_oom_score_
.erase(handle
);
490 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
: {
491 bool visible
= *content::Details
<bool>(details
).ptr();
494 content::Source
<content::RenderWidgetHost
>(source
).ptr()->
495 GetProcess()->GetHandle();
497 // If the currently focused tab already has a lower score, do not
498 // set it. This can happen in case the newly focused tab is script
499 // connected to the previous tab.
500 ProcessScoreMap::iterator it
;
501 it
= pid_to_oom_score_
.find(focused_tab_pid_
);
502 if (it
== pid_to_oom_score_
.end()
503 || it
->second
!= chrome::kLowestRendererOomScore
) {
504 // By starting a timer we guarantee that the tab is focused for
505 // certain amount of time. Secondly, it also does not add overhead
506 // to the tab switching time.
507 if (focus_tab_score_adjust_timer_
.IsRunning())
508 focus_tab_score_adjust_timer_
.Reset();
510 focus_tab_score_adjust_timer_
.Start(FROM_HERE
,
511 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs
),
512 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout
);
518 NOTREACHED() << L
"Received unexpected notification";
523 // Here we collect most of the information we need to sort the
524 // existing renderers in priority order, and hand out oom_score_adj
525 // scores based on that sort order.
527 // Things we need to collect on the browser thread (because
528 // TabStripModel isn't thread safe):
529 // 1) whether or not a tab is pinned
530 // 2) last time a tab was selected
531 // 3) is the tab currently selected
532 void OomPriorityManager::AdjustOomPriorities() {
533 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
)->empty())
536 // Check for a discontinuity in time caused by the machine being suspended.
537 if (!last_adjust_time_
.is_null()) {
538 TimeDelta suspend_time
= TimeTicks::Now() - last_adjust_time_
;
539 if (suspend_time
.InSeconds() > kSuspendThresholdSeconds
) {
540 // We were probably suspended, move our event timers forward in time so
541 // when we subtract them out later we are counting "uptime".
542 start_time_
+= suspend_time
;
543 if (!last_discard_time_
.is_null())
544 last_discard_time_
+= suspend_time
;
547 last_adjust_time_
= TimeTicks::Now();
549 TabStatsList stats_list
= GetTabStatsOnUIThread();
550 BrowserThread::PostTask(
551 BrowserThread::FILE, FROM_HERE
,
552 base::Bind(&OomPriorityManager::AdjustOomPrioritiesOnFileThread
,
553 base::Unretained(this), stats_list
));
556 OomPriorityManager::TabStatsList
OomPriorityManager::GetTabStatsOnUIThread() {
557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
558 TabStatsList stats_list
;
559 stats_list
.reserve(32); // 99% of users have < 30 tabs open
560 bool browser_active
= true;
561 const BrowserList
* ash_browser_list
=
562 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
563 for (BrowserList::const_reverse_iterator browser_iterator
=
564 ash_browser_list
->begin_last_active();
565 browser_iterator
!= ash_browser_list
->end_last_active();
566 ++browser_iterator
) {
567 Browser
* browser
= *browser_iterator
;
568 bool is_browser_for_app
= browser
->is_app();
569 const TabStripModel
* model
= browser
->tab_strip_model();
570 for (int i
= 0; i
< model
->count(); i
++) {
571 WebContents
* contents
= model
->GetWebContentsAt(i
);
572 if (!contents
->IsCrashed()) {
574 stats
.is_app
= is_browser_for_app
;
575 stats
.is_reloadable_ui
=
576 IsReloadableUI(contents
->GetLastCommittedURL());
577 stats
.is_playing_audio
= chrome::IsPlayingAudio(contents
);
578 stats
.is_pinned
= model
->IsTabPinned(i
);
579 stats
.is_selected
= browser_active
&& model
->IsTabSelected(i
);
580 stats
.is_discarded
= model
->IsTabDiscarded(i
);
581 stats
.last_selected
= contents
->GetLastSelectedTime();
582 stats
.renderer_handle
= contents
->GetRenderProcessHost()->GetHandle();
583 stats
.title
= contents
->GetTitle();
584 stats
.tab_contents_id
= IdFromWebContents(contents
);
585 stats_list
.push_back(stats
);
588 // We process the active browser window in the first iteration.
589 browser_active
= false;
591 // Sort the data we collected so that least desirable to be
592 // killed is first, most desirable is last.
593 std::sort(stats_list
.begin(), stats_list
.end(), CompareTabStats
);
597 void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
598 TabStatsList stats_list
) {
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
600 base::AutoLock
pid_to_oom_score_autolock(pid_to_oom_score_lock_
);
602 // Now we assign priorities based on the sorted list. We're
603 // assigning priorities in the range of kLowestRendererOomScore to
604 // kHighestRendererOomScore (defined in chrome_constants.h).
605 // oom_score_adj takes values from -1000 to 1000. Negative values
606 // are reserved for system processes, and we want to give some room
607 // below the range we're using to allow for things that want to be
608 // above the renderers in priority, so the defined range gives us
609 // some variation in priority without taking up the whole range. In
610 // the end, however, it's a pretty arbitrary range to use. Higher
611 // values are more likely to be killed by the OOM killer.
613 // We also remove any duplicate PIDs, leaving the most important
614 // (least likely to be killed) of the duplicates, so that a
615 // particular renderer process takes on the oom_score_adj of the
616 // least likely tab to be killed.
617 const int kPriorityRange
= chrome::kHighestRendererOomScore
-
618 chrome::kLowestRendererOomScore
;
619 float priority_increment
=
620 static_cast<float>(kPriorityRange
) / stats_list
.size();
621 float priority
= chrome::kLowestRendererOomScore
;
622 std::set
<base::ProcessHandle
> already_seen
;
624 ProcessScoreMap::iterator it
;
625 for (TabStatsList::iterator iterator
= stats_list
.begin();
626 iterator
!= stats_list
.end(); ++iterator
) {
627 // stats_list also contains discarded tab stat. If renderer_handler is zero,
628 // we don't need to adjust oom_score.
629 if (iterator
->renderer_handle
== 0)
631 if (already_seen
.find(iterator
->renderer_handle
) == already_seen
.end()) {
632 already_seen
.insert(iterator
->renderer_handle
);
633 // If a process has the same score as the newly calculated value,
635 score
= static_cast<int>(priority
+ 0.5f
);
636 it
= pid_to_oom_score_
.find(iterator
->renderer_handle
);
637 if (it
== pid_to_oom_score_
.end() || it
->second
!= score
) {
638 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
639 iterator
->renderer_handle
, score
);
640 pid_to_oom_score_
[iterator
->renderer_handle
] = score
;
642 priority
+= priority_increment
;
647 void OomPriorityManager::OnMemoryLow() {
648 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
649 LogMemoryAndDiscardTab();
652 } // namespace chromeos