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/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/memory/memory_pressure_monitor.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/metrics/histogram.h"
20 #include "base/process/process.h"
21 #include "base/strings/string16.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/lock.h"
26 #include "base/threading/thread.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/chromeos/memory/low_memory_observer.h"
31 #include "chrome/browser/chromeos/memory/oom_memory_details.h"
32 #include "chrome/browser/chromeos/memory/system_memory_stats_recorder.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_iterator.h"
35 #include "chrome/browser/ui/browser_list.h"
36 #include "chrome/browser/ui/host_desktop.h"
37 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
39 #include "chrome/browser/ui/tabs/tab_utils.h"
40 #include "chrome/common/chrome_constants.h"
41 #include "chrome/common/url_constants.h"
42 #include "chromeos/chromeos_switches.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/notification_service.h"
45 #include "content/public/browser/notification_types.h"
46 #include "content/public/browser/render_process_host.h"
47 #include "content/public/browser/render_widget_host.h"
48 #include "content/public/browser/web_contents.h"
49 #include "content/public/browser/zygote_host_linux.h"
51 using base::TimeDelta
;
52 using base::TimeTicks
;
53 using content::BrowserThread
;
54 using content::WebContents
;
60 // The default interval in seconds after which to adjust the oom_score_adj
62 const int kAdjustmentIntervalSeconds
= 10;
64 // For each period of this length we record a statistic to indicate whether
65 // or not the user experienced a low memory event. If you change this interval
66 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic.
67 const int kRecentTabDiscardIntervalSeconds
= 60;
69 // If there has been no priority adjustment in this interval, we assume the
70 // machine was suspended and correct our timing statistics.
71 const int kSuspendThresholdSeconds
= kAdjustmentIntervalSeconds
* 4;
73 // When switching to a new tab the tab's renderer's OOM score needs to be
74 // updated to reflect its front-most status and protect it from discard.
75 // However, doing this immediately might slow down tab switch time, so wait
76 // a little while before doing the adjustment.
77 const int kFocusedTabScoreAdjustIntervalMs
= 500;
79 // Returns a unique ID for a WebContents. Do not cast back to a pointer, as
80 // the WebContents could be deleted if the user closed the tab.
81 int64
IdFromWebContents(WebContents
* web_contents
) {
82 return reinterpret_cast<int64
>(web_contents
);
87 ////////////////////////////////////////////////////////////////////////////////
90 OomPriorityManager::TabStats::TabStats()
92 is_reloadable_ui(false),
93 is_playing_audio(false),
101 OomPriorityManager::TabStats::~TabStats() {
104 OomPriorityManager::OomPriorityManager()
105 : focused_tab_process_info_(std::make_pair(0, 0)),
107 recent_tab_discard_(false) {
108 // Use the old |LowMemoryObserver| when there is no |MemoryPressureMonitor|.
109 if (!base::MemoryPressureMonitor::Get())
110 low_memory_observer_
.reset(new LowMemoryObserver
);
113 content::NOTIFICATION_RENDERER_PROCESS_CLOSED
,
114 content::NotificationService::AllBrowserContextsAndSources());
116 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
117 content::NotificationService::AllBrowserContextsAndSources());
119 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
120 content::NotificationService::AllBrowserContextsAndSources());
123 OomPriorityManager::~OomPriorityManager() {
127 void OomPriorityManager::Start() {
128 if (!timer_
.IsRunning()) {
129 timer_
.Start(FROM_HERE
,
130 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds
),
132 &OomPriorityManager::AdjustOomPriorities
);
134 if (!recent_tab_discard_timer_
.IsRunning()) {
135 recent_tab_discard_timer_
.Start(
137 TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds
),
139 &OomPriorityManager::RecordRecentTabDiscard
);
141 start_time_
= TimeTicks::Now();
142 // If a |LowMemoryObserver| exists we use the old system, otherwise we create
143 // a |MemoryPressureListener| to listen for memory events.
144 if (low_memory_observer_
) {
145 low_memory_observer_
->Start();
147 base::MemoryPressureMonitor
* monitor
= base::MemoryPressureMonitor::Get();
149 memory_pressure_listener_
.reset(new base::MemoryPressureListener(
150 base::Bind(&OomPriorityManager::OnMemoryPressure
,
151 base::Unretained(this))));
152 base::MemoryPressureListener::MemoryPressureLevel level
=
153 monitor
->GetCurrentPressureLevel();
155 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
) {
156 OnMemoryPressure(level
);
162 void OomPriorityManager::Stop() {
164 recent_tab_discard_timer_
.Stop();
165 if (low_memory_observer_
)
166 low_memory_observer_
->Stop();
168 memory_pressure_listener_
.reset();
171 std::vector
<base::string16
> OomPriorityManager::GetTabTitles() {
172 TabStatsList stats
= GetTabStatsOnUIThread();
173 base::AutoLock
oom_score_autolock(oom_score_lock_
);
174 std::vector
<base::string16
> titles
;
175 titles
.reserve(stats
.size());
176 TabStatsList::iterator it
= stats
.begin();
177 for ( ; it
!= stats
.end(); ++it
) {
180 int score
= oom_score_map_
[it
->child_process_host_id
];
181 str
+= base::IntToString16(score
);
182 str
+= base::ASCIIToUTF16(" - ");
184 str
+= base::ASCIIToUTF16(it
->is_app
? " app" : "");
185 str
+= base::ASCIIToUTF16(it
->is_reloadable_ui
? " reloadable_ui" : "");
186 str
+= base::ASCIIToUTF16(it
->is_playing_audio
? " playing_audio" : "");
187 str
+= base::ASCIIToUTF16(it
->is_pinned
? " pinned" : "");
188 str
+= base::ASCIIToUTF16(it
->is_discarded
? " discarded" : "");
189 titles
.push_back(str
);
194 // TODO(jamescook): This should consider tabs with references to other tabs,
195 // such as tabs created with JavaScript window.open(). We might want to
196 // discard the entire set together, or use that in the priority computation.
197 bool OomPriorityManager::DiscardTab() {
198 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
199 TabStatsList stats
= GetTabStatsOnUIThread();
202 // Loop until we find a non-discarded tab to kill.
203 for (TabStatsList::const_reverse_iterator stats_rit
= stats
.rbegin();
204 stats_rit
!= stats
.rend();
206 int64 least_important_tab_id
= stats_rit
->tab_contents_id
;
207 if (DiscardTabById(least_important_tab_id
))
213 void OomPriorityManager::LogMemoryAndDiscardTab() {
214 LogMemory("Tab Discards Memory details",
215 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTabs
));
218 void OomPriorityManager::LogMemory(const std::string
& title
,
219 const base::Closure
& callback
) {
220 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
221 OomMemoryDetails::Log(title
, callback
);
224 ///////////////////////////////////////////////////////////////////////////////
225 // OomPriorityManager, private:
228 void OomPriorityManager::PurgeMemoryAndDiscardTabs() {
229 if (g_browser_process
&&
230 g_browser_process
->platform_part()->oom_priority_manager()) {
231 OomPriorityManager
* manager
=
232 g_browser_process
->platform_part()->oom_priority_manager();
233 manager
->PurgeBrowserMemory();
234 manager
->DiscardTab();
239 bool OomPriorityManager::IsReloadableUI(const GURL
& url
) {
240 // There are many chrome:// UI URLs, but only look for the ones that users
241 // are likely to have open. Most of the benefit is the from NTP URL.
242 const char* const kReloadableUrlPrefixes
[] = {
243 chrome::kChromeUIDownloadsURL
,
244 chrome::kChromeUIHistoryURL
,
245 chrome::kChromeUINewTabURL
,
246 chrome::kChromeUISettingsURL
,
248 // Prefix-match against the table above. Use strncmp to avoid allocating
249 // memory to convert the URL prefix constants into std::strings.
250 for (size_t i
= 0; i
< arraysize(kReloadableUrlPrefixes
); ++i
) {
251 if (!strncmp(url
.spec().c_str(),
252 kReloadableUrlPrefixes
[i
],
253 strlen(kReloadableUrlPrefixes
[i
])))
259 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id
) {
260 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
261 Browser
* browser
= *it
;
262 TabStripModel
* model
= browser
->tab_strip_model();
263 for (int idx
= 0; idx
< model
->count(); idx
++) {
264 // Can't discard tabs that are already discarded or active.
265 if (model
->IsTabDiscarded(idx
) || (model
->active_index() == idx
))
267 WebContents
* web_contents
= model
->GetWebContentsAt(idx
);
268 int64 web_contents_id
= IdFromWebContents(web_contents
);
269 if (web_contents_id
== target_web_contents_id
) {
270 LOG(WARNING
) << "Discarding tab " << idx
271 << " id " << target_web_contents_id
;
272 // Record statistics before discarding because we want to capture the
273 // memory state that lead to the discard.
274 RecordDiscardStatistics();
275 model
->DiscardWebContentsAt(idx
);
276 recent_tab_discard_
= true;
284 void OomPriorityManager::RecordDiscardStatistics() {
285 // Record a raw count so we can compare to discard reloads.
287 UMA_HISTOGRAM_CUSTOM_COUNTS(
288 "Tabs.Discard.DiscardCount", discard_count_
, 1, 1000, 50);
290 // TODO(jamescook): Maybe incorporate extension count?
291 UMA_HISTOGRAM_CUSTOM_COUNTS(
292 "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50);
293 // Record the discarded tab in relation to the amount of simultaneously
295 ash::MultiProfileUMA::RecordDiscardedTab(
296 ash::Shell::GetInstance()->session_state_delegate()->
297 NumberOfLoggedInUsers());
299 // TODO(jamescook): If the time stats prove too noisy, then divide up users
300 // based on how heavily they use Chrome using tab count as a proxy.
301 // Bin into <= 1, <= 2, <= 4, <= 8, etc.
302 if (last_discard_time_
.is_null()) {
303 // This is the first discard this session.
304 TimeDelta interval
= TimeTicks::Now() - start_time_
;
305 int interval_seconds
= static_cast<int>(interval
.InSeconds());
306 // Record time in seconds over an interval of approximately 1 day.
307 UMA_HISTOGRAM_CUSTOM_COUNTS(
308 "Tabs.Discard.InitialTime2", interval_seconds
, 1, 100000, 50);
310 // Not the first discard, so compute time since last discard.
311 TimeDelta interval
= TimeTicks::Now() - last_discard_time_
;
312 int interval_ms
= static_cast<int>(interval
.InMilliseconds());
313 // Record time in milliseconds over an interval of approximately 1 day.
314 // Start at 100 ms to get extra resolution in the target 750 ms range.
315 UMA_HISTOGRAM_CUSTOM_COUNTS(
316 "Tabs.Discard.IntervalTime2", interval_ms
, 100, 100000 * 1000, 50);
318 // Record chromeos's concept of system memory usage at the time of the
320 RecordMemoryStats(RECORD_MEMORY_STATS_TAB_DISCARDED
);
322 // Set up to record the next interval.
323 last_discard_time_
= TimeTicks::Now();
326 void OomPriorityManager::RecordRecentTabDiscard() {
327 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
328 // If we change the interval we need to change the histogram name.
329 UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute",
330 recent_tab_discard_
);
331 // Reset for the next interval.
332 recent_tab_discard_
= false;
335 void OomPriorityManager::PurgeBrowserMemory() {
336 // Based on experimental evidence, attempts to free memory from renderers
337 // have been too slow to use in OOM situations (V8 garbage collection) or
338 // do not lead to persistent decreased usage (image/bitmap caches). This
339 // function therefore only targets large blocks of memory in the browser.
340 // Note that other objects will listen to MemoryPressureListener events
341 // to release memory.
342 for (TabContentsIterator it
; !it
.done(); it
.Next()) {
343 WebContents
* web_contents
= *it
;
344 // Screenshots can consume ~5 MB per web contents for platforms that do
345 // touch back/forward.
346 web_contents
->GetController().ClearAllScreenshots();
350 int OomPriorityManager::GetTabCount() const {
352 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
353 tab_count
+= it
->tab_strip_model()->count();
357 // Returns true if |first| is considered less desirable to be killed
359 bool OomPriorityManager::CompareTabStats(TabStats first
,
361 // Being currently selected is most important to protect.
362 if (first
.is_selected
!= second
.is_selected
)
363 return first
.is_selected
;
365 // Tab with internal web UI like NTP or Settings are good choices to discard,
366 // so protect non-Web UI and let the other conditionals finish the sort.
367 if (first
.is_reloadable_ui
!= second
.is_reloadable_ui
)
368 return !first
.is_reloadable_ui
;
370 // Being pinned is important to protect.
371 if (first
.is_pinned
!= second
.is_pinned
)
372 return first
.is_pinned
;
374 // Being an app is important too, as you're the only visible surface in the
375 // window and we don't want to discard that.
376 if (first
.is_app
!= second
.is_app
)
379 // Protect streaming audio and video conferencing tabs.
380 if (first
.is_playing_audio
!= second
.is_playing_audio
)
381 return first
.is_playing_audio
;
383 // TODO(jamescook): Incorporate sudden_termination_allowed into the sort
384 // order. We don't do this now because pages with unload handlers set
385 // sudden_termination_allowed false, and that covers too many common pages
386 // with ad networks and statistics scripts. Ideally we would like to check
387 // for beforeUnload handlers, which are likely to present a dialog asking
388 // if the user wants to discard state. crbug.com/123049
390 // Being more recently active is more important.
391 return first
.last_active
> second
.last_active
;
394 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
395 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
396 base::AutoLock
oom_score_autolock(oom_score_lock_
);
397 base::ProcessHandle pid
= focused_tab_process_info_
.second
;
398 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
399 pid
, chrome::kLowestRendererOomScore
);
400 oom_score_map_
[focused_tab_process_info_
.first
] =
401 chrome::kLowestRendererOomScore
;
404 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
405 BrowserThread::PostTask(
406 BrowserThread::FILE, FROM_HERE
,
407 base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread
,
408 base::Unretained(this)));
411 void OomPriorityManager::Observe(int type
,
412 const content::NotificationSource
& source
,
413 const content::NotificationDetails
& details
) {
414 base::AutoLock
oom_score_autolock(oom_score_lock_
);
416 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED
:
417 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED
: {
418 content::RenderProcessHost
* host
=
419 content::Source
<content::RenderProcessHost
>(source
).ptr();
420 oom_score_map_
.erase(host
->GetID());
421 if (!low_memory_observer_
) {
422 // Coming here we know that a renderer was just killed and memory should
423 // come back into the pool. However - the memory pressure observer did
424 // not yet update its status and therefore we ask it to redo the
425 // measurement, calling us again if we have to release more.
426 // Note: We do not only accelerate the discarding speed by doing another
427 // check in short succession - we also accelerate it because the timer
428 // driven MemoryPressureMonitor will continue to produce timed events
429 // on top. So the longer the cleanup phase takes, the more tabs will
430 // get discarded in parallel.
431 base::chromeos::MemoryPressureMonitor
* monitor
=
432 base::chromeos::MemoryPressureMonitor::Get();
434 monitor
->ScheduleEarlyCheck();
438 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
: {
439 bool visible
= *content::Details
<bool>(details
).ptr();
441 content::RenderProcessHost
* render_host
=
442 content::Source
<content::RenderWidgetHost
>(source
).ptr()->
444 focused_tab_process_info_
= std::make_pair(render_host
->GetID(),
445 render_host
->GetHandle());
447 // If the currently focused tab already has a lower score, do not
448 // set it. This can happen in case the newly focused tab is script
449 // connected to the previous tab.
450 ProcessScoreMap::iterator it
;
451 it
= oom_score_map_
.find(focused_tab_process_info_
.first
);
452 if (it
== oom_score_map_
.end()
453 || it
->second
!= chrome::kLowestRendererOomScore
) {
454 // By starting a timer we guarantee that the tab is focused for
455 // certain amount of time. Secondly, it also does not add overhead
456 // to the tab switching time.
457 if (focus_tab_score_adjust_timer_
.IsRunning())
458 focus_tab_score_adjust_timer_
.Reset();
460 focus_tab_score_adjust_timer_
.Start(FROM_HERE
,
461 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs
),
462 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout
);
468 NOTREACHED() << L
"Received unexpected notification";
473 // Here we collect most of the information we need to sort the
474 // existing renderers in priority order, and hand out oom_score_adj
475 // scores based on that sort order.
477 // Things we need to collect on the browser thread (because
478 // TabStripModel isn't thread safe):
479 // 1) whether or not a tab is pinned
480 // 2) last time a tab was selected
481 // 3) is the tab currently selected
482 void OomPriorityManager::AdjustOomPriorities() {
483 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
)->empty())
486 // Check for a discontinuity in time caused by the machine being suspended.
487 if (!last_adjust_time_
.is_null()) {
488 TimeDelta suspend_time
= TimeTicks::Now() - last_adjust_time_
;
489 if (suspend_time
.InSeconds() > kSuspendThresholdSeconds
) {
490 // We were probably suspended, move our event timers forward in time so
491 // when we subtract them out later we are counting "uptime".
492 start_time_
+= suspend_time
;
493 if (!last_discard_time_
.is_null())
494 last_discard_time_
+= suspend_time
;
497 last_adjust_time_
= TimeTicks::Now();
499 TabStatsList stats_list
= GetTabStatsOnUIThread();
500 BrowserThread::PostTask(
501 BrowserThread::FILE, FROM_HERE
,
502 base::Bind(&OomPriorityManager::AdjustOomPrioritiesOnFileThread
,
503 base::Unretained(this), stats_list
));
506 OomPriorityManager::TabStatsList
OomPriorityManager::GetTabStatsOnUIThread() {
507 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
508 TabStatsList stats_list
;
509 stats_list
.reserve(32); // 99% of users have < 30 tabs open
510 bool browser_active
= true;
511 const BrowserList
* ash_browser_list
=
512 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
513 for (BrowserList::const_reverse_iterator browser_iterator
=
514 ash_browser_list
->begin_last_active();
515 browser_iterator
!= ash_browser_list
->end_last_active();
516 ++browser_iterator
) {
517 Browser
* browser
= *browser_iterator
;
518 bool is_browser_for_app
= browser
->is_app();
519 const TabStripModel
* model
= browser
->tab_strip_model();
520 for (int i
= 0; i
< model
->count(); i
++) {
521 WebContents
* contents
= model
->GetWebContentsAt(i
);
522 if (!contents
->IsCrashed()) {
524 stats
.is_app
= is_browser_for_app
;
525 stats
.is_reloadable_ui
=
526 IsReloadableUI(contents
->GetLastCommittedURL());
527 stats
.is_playing_audio
= chrome::IsPlayingAudio(contents
);
528 stats
.is_pinned
= model
->IsTabPinned(i
);
529 stats
.is_selected
= browser_active
&& model
->IsTabSelected(i
);
530 stats
.is_discarded
= model
->IsTabDiscarded(i
);
531 stats
.last_active
= contents
->GetLastActiveTime();
532 stats
.renderer_handle
= contents
->GetRenderProcessHost()->GetHandle();
533 stats
.child_process_host_id
= contents
->GetRenderProcessHost()->GetID();
534 stats
.title
= contents
->GetTitle();
535 stats
.tab_contents_id
= IdFromWebContents(contents
);
536 stats_list
.push_back(stats
);
539 // We process the active browser window in the first iteration.
540 browser_active
= false;
542 // Sort the data we collected so that least desirable to be
543 // killed is first, most desirable is last.
544 std::sort(stats_list
.begin(), stats_list
.end(), CompareTabStats
);
549 std::vector
<OomPriorityManager::ProcessInfo
>
550 OomPriorityManager::GetChildProcessInfos(
551 const TabStatsList
& stats_list
) {
552 std::vector
<ProcessInfo
> process_infos
;
553 std::set
<base::ProcessHandle
> already_seen
;
554 for (TabStatsList::const_iterator iterator
= stats_list
.begin();
555 iterator
!= stats_list
.end(); ++iterator
) {
556 // stats_list contains entries for already-discarded tabs. If the PID
557 // (renderer_handle) is zero, we don't need to adjust the oom_score.
558 if (iterator
->renderer_handle
== 0)
561 bool inserted
= already_seen
.insert(iterator
->renderer_handle
).second
;
563 // We've already seen this process handle.
567 process_infos
.push_back(std::make_pair(
568 iterator
->child_process_host_id
, iterator
->renderer_handle
));
570 return process_infos
;
573 void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
574 TabStatsList stats_list
) {
575 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
576 base::AutoLock
oom_score_autolock(oom_score_lock_
);
578 // Remove any duplicate PIDs. Order of the list is maintained, so each
579 // renderer process will take on the oom_score_adj of the most important
580 // (least likely to be killed) tab.
581 std::vector
<ProcessInfo
> process_infos
= GetChildProcessInfos(stats_list
);
583 // Now we assign priorities based on the sorted list. We're
584 // assigning priorities in the range of kLowestRendererOomScore to
585 // kHighestRendererOomScore (defined in chrome_constants.h).
586 // oom_score_adj takes values from -1000 to 1000. Negative values
587 // are reserved for system processes, and we want to give some room
588 // below the range we're using to allow for things that want to be
589 // above the renderers in priority, so the defined range gives us
590 // some variation in priority without taking up the whole range. In
591 // the end, however, it's a pretty arbitrary range to use. Higher
592 // values are more likely to be killed by the OOM killer.
593 float priority
= chrome::kLowestRendererOomScore
;
594 const int kPriorityRange
= chrome::kHighestRendererOomScore
-
595 chrome::kLowestRendererOomScore
;
596 float priority_increment
=
597 static_cast<float>(kPriorityRange
) / process_infos
.size();
598 for (const auto& process_info
: process_infos
) {
599 int score
= static_cast<int>(priority
+ 0.5f
);
600 ProcessScoreMap::iterator it
=
601 oom_score_map_
.find(process_info
.first
);
602 // If a process has the same score as the newly calculated value,
604 if (it
== oom_score_map_
.end() || it
->second
!= score
) {
605 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
606 process_info
.second
, score
);
607 oom_score_map_
[process_info
.first
] = score
;
609 priority
+= priority_increment
;
613 void OomPriorityManager::OnMemoryPressure(
614 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level
) {
615 // For the moment we only do something when we reach a critical state.
616 if (memory_pressure_level
==
617 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL
) {
618 LogMemoryAndDiscardTab();
620 // TODO(skuhne): If more memory pressure levels are introduced, we might
621 // consider to call PurgeBrowserMemory() before CRITICAL is reached.
624 } // namespace chromeos