OLD | NEW |
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/chromeos/memory/oom_priority_manager.h" | 5 #include "chrome/browser/chromeos/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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 } | 65 } |
66 | 66 |
67 // Record a size in megabytes, over a potential interval up to 32 GB. | 67 // Record a size in megabytes, over a potential interval up to 32 GB. |
68 #define EXPERIMENT_HISTOGRAM_MEGABYTES(name, sample) \ | 68 #define EXPERIMENT_HISTOGRAM_MEGABYTES(name, sample) \ |
69 EXPERIMENT_CUSTOM_COUNTS(name, sample, 1, 32768, 50) | 69 EXPERIMENT_CUSTOM_COUNTS(name, sample, 1, 32768, 50) |
70 | 70 |
71 // The default interval in seconds after which to adjust the oom_score_adj | 71 // The default interval in seconds after which to adjust the oom_score_adj |
72 // value. | 72 // value. |
73 const int kAdjustmentIntervalSeconds = 10; | 73 const int kAdjustmentIntervalSeconds = 10; |
74 | 74 |
| 75 // For each period of this length we record a statistic to indicate whether |
| 76 // or not the user experienced a low memory event. If you change this interval |
| 77 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic. |
| 78 const int kRecentTabDiscardIntervalSeconds = 60; |
| 79 |
75 // If there has been no priority adjustment in this interval, we assume the | 80 // If there has been no priority adjustment in this interval, we assume the |
76 // machine was suspended and correct our timing statistics. | 81 // machine was suspended and correct our timing statistics. |
77 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; | 82 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; |
78 | 83 |
79 // When switching to a new tab the tab's renderer's OOM score needs to be | 84 // When switching to a new tab the tab's renderer's OOM score needs to be |
80 // updated to reflect its front-most status and protect it from discard. | 85 // updated to reflect its front-most status and protect it from discard. |
81 // However, doing this immediately might slow down tab switch time, so wait | 86 // However, doing this immediately might slow down tab switch time, so wait |
82 // a little while before doing the adjustment. | 87 // a little while before doing the adjustment. |
83 const int kFocusedTabScoreAdjustIntervalMs = 500; | 88 const int kFocusedTabScoreAdjustIntervalMs = 500; |
84 | 89 |
85 // Returns a unique ID for a WebContents. Do not cast back to a pointer, as | 90 // Returns a unique ID for a WebContents. Do not cast back to a pointer, as |
86 // the WebContents could be deleted if the user closed the tab. | 91 // the WebContents could be deleted if the user closed the tab. |
87 int64 IdFromWebContents(WebContents* web_contents) { | 92 int64 IdFromWebContents(WebContents* web_contents) { |
88 return reinterpret_cast<int64>(web_contents); | 93 return reinterpret_cast<int64>(web_contents); |
89 } | 94 } |
90 | 95 |
| 96 // Records a statistics |sample| for UMA histogram |name| using a linear |
| 97 // distribution of buckets. |
| 98 void RecordLinearHistogram(const std::string& name, |
| 99 int sample, |
| 100 int maximum, |
| 101 size_t bucket_count) { |
| 102 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram |
| 103 // instance and thus only work if |name| is constant. |
| 104 base::HistogramBase* counter = base::LinearHistogram::FactoryGet( |
| 105 name, |
| 106 1, // Minimum. The 0 bin for underflow is automatically added. |
| 107 maximum + 1, // Ensure bucket size of |maximum| / |bucket_count|. |
| 108 bucket_count + 2, // Account for the underflow and overflow bins. |
| 109 base::Histogram::kUmaTargetedHistogramFlag); |
| 110 counter->Add(sample); |
| 111 } |
| 112 |
91 } // namespace | 113 } // namespace |
92 | 114 |
93 //////////////////////////////////////////////////////////////////////////////// | 115 //////////////////////////////////////////////////////////////////////////////// |
94 // OomMemoryDetails logs details about all Chrome processes during an out-of- | 116 // OomMemoryDetails logs details about all Chrome processes during an out-of- |
95 // memory event in an attempt to identify the culprit, then discards a tab and | 117 // memory event in an attempt to identify the culprit, then discards a tab and |
96 // deletes itself. | 118 // deletes itself. |
97 class OomMemoryDetails : public MemoryDetails { | 119 class OomMemoryDetails : public MemoryDetails { |
98 public: | 120 public: |
99 OomMemoryDetails(); | 121 OomMemoryDetails(); |
100 | 122 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 is_discarded(false), | 169 is_discarded(false), |
148 renderer_handle(0), | 170 renderer_handle(0), |
149 tab_contents_id(0) { | 171 tab_contents_id(0) { |
150 } | 172 } |
151 | 173 |
152 OomPriorityManager::TabStats::~TabStats() { | 174 OomPriorityManager::TabStats::~TabStats() { |
153 } | 175 } |
154 | 176 |
155 OomPriorityManager::OomPriorityManager() | 177 OomPriorityManager::OomPriorityManager() |
156 : focused_tab_pid_(0), | 178 : focused_tab_pid_(0), |
157 discard_count_(0) { | 179 discard_count_(0), |
| 180 recent_tab_discard_(false) { |
158 // We only need the low memory observer if we want to discard tabs. | 181 // We only need the low memory observer if we want to discard tabs. |
159 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoDiscardTabs)) | 182 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoDiscardTabs)) |
160 low_memory_observer_.reset(new LowMemoryObserver); | 183 low_memory_observer_.reset(new LowMemoryObserver); |
161 | 184 |
162 registrar_.Add(this, | 185 registrar_.Add(this, |
163 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 186 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
164 content::NotificationService::AllBrowserContextsAndSources()); | 187 content::NotificationService::AllBrowserContextsAndSources()); |
165 registrar_.Add(this, | 188 registrar_.Add(this, |
166 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | 189 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
167 content::NotificationService::AllBrowserContextsAndSources()); | 190 content::NotificationService::AllBrowserContextsAndSources()); |
168 registrar_.Add(this, | 191 registrar_.Add(this, |
169 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | 192 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
170 content::NotificationService::AllBrowserContextsAndSources()); | 193 content::NotificationService::AllBrowserContextsAndSources()); |
171 } | 194 } |
172 | 195 |
173 OomPriorityManager::~OomPriorityManager() { | 196 OomPriorityManager::~OomPriorityManager() { |
174 Stop(); | 197 Stop(); |
175 } | 198 } |
176 | 199 |
177 void OomPriorityManager::Start() { | 200 void OomPriorityManager::Start() { |
178 if (!timer_.IsRunning()) { | 201 if (!timer_.IsRunning()) { |
179 timer_.Start(FROM_HERE, | 202 timer_.Start(FROM_HERE, |
180 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds), | 203 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds), |
181 this, | 204 this, |
182 &OomPriorityManager::AdjustOomPriorities); | 205 &OomPriorityManager::AdjustOomPriorities); |
183 } | 206 } |
| 207 if (!recent_tab_discard_timer_.IsRunning()) { |
| 208 recent_tab_discard_timer_.Start( |
| 209 FROM_HERE, |
| 210 TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds), |
| 211 this, |
| 212 &OomPriorityManager::RecordRecentTabDiscard); |
| 213 } |
184 if (low_memory_observer_.get()) | 214 if (low_memory_observer_.get()) |
185 low_memory_observer_->Start(); | 215 low_memory_observer_->Start(); |
186 start_time_ = TimeTicks::Now(); | 216 start_time_ = TimeTicks::Now(); |
187 } | 217 } |
188 | 218 |
189 void OomPriorityManager::Stop() { | 219 void OomPriorityManager::Stop() { |
190 timer_.Stop(); | 220 timer_.Stop(); |
| 221 recent_tab_discard_timer_.Stop(); |
191 if (low_memory_observer_.get()) | 222 if (low_memory_observer_.get()) |
192 low_memory_observer_->Stop(); | 223 low_memory_observer_->Stop(); |
193 } | 224 } |
194 | 225 |
195 std::vector<string16> OomPriorityManager::GetTabTitles() { | 226 std::vector<string16> OomPriorityManager::GetTabTitles() { |
196 TabStatsList stats = GetTabStatsOnUIThread(); | 227 TabStatsList stats = GetTabStatsOnUIThread(); |
197 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); | 228 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); |
198 std::vector<string16> titles; | 229 std::vector<string16> titles; |
199 titles.reserve(stats.size()); | 230 titles.reserve(stats.size()); |
200 TabStatsList::iterator it = stats.begin(); | 231 TabStatsList::iterator it = stats.begin(); |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 continue; | 306 continue; |
276 WebContents* web_contents = model->GetWebContentsAt(idx); | 307 WebContents* web_contents = model->GetWebContentsAt(idx); |
277 int64 web_contents_id = IdFromWebContents(web_contents); | 308 int64 web_contents_id = IdFromWebContents(web_contents); |
278 if (web_contents_id == target_web_contents_id) { | 309 if (web_contents_id == target_web_contents_id) { |
279 LOG(WARNING) << "Discarding tab " << idx | 310 LOG(WARNING) << "Discarding tab " << idx |
280 << " id " << target_web_contents_id; | 311 << " id " << target_web_contents_id; |
281 // Record statistics before discarding because we want to capture the | 312 // Record statistics before discarding because we want to capture the |
282 // memory state that lead to the discard. | 313 // memory state that lead to the discard. |
283 RecordDiscardStatistics(); | 314 RecordDiscardStatistics(); |
284 model->DiscardWebContentsAt(idx); | 315 model->DiscardWebContentsAt(idx); |
| 316 recent_tab_discard_ = true; |
285 return true; | 317 return true; |
286 } | 318 } |
287 } | 319 } |
288 } | 320 } |
289 return false; | 321 return false; |
290 } | 322 } |
291 | 323 |
292 void OomPriorityManager::RecordDiscardStatistics() { | 324 void OomPriorityManager::RecordDiscardStatistics() { |
293 // Record a raw count so we can compare to discard reloads. | 325 // Record a raw count so we can compare to discard reloads. |
294 discard_count_++; | 326 discard_count_++; |
(...skipping 25 matching lines...) Expand all Loading... |
320 // Record Chrome's concept of system memory usage at the time of the discard. | 352 // Record Chrome's concept of system memory usage at the time of the discard. |
321 base::SystemMemoryInfoKB memory; | 353 base::SystemMemoryInfoKB memory; |
322 if (base::GetSystemMemoryInfo(&memory)) { | 354 if (base::GetSystemMemoryInfo(&memory)) { |
323 // TODO(jamescook): Remove this after R25 is deployed to stable. It does | 355 // TODO(jamescook): Remove this after R25 is deployed to stable. It does |
324 // not have sufficient resolution in the 2-4 GB range and does not properly | 356 // not have sufficient resolution in the 2-4 GB range and does not properly |
325 // account for graphics memory on ARM. Replace with MemAllocatedMB below. | 357 // account for graphics memory on ARM. Replace with MemAllocatedMB below. |
326 int mem_anonymous_mb = (memory.active_anon + memory.inactive_anon) / 1024; | 358 int mem_anonymous_mb = (memory.active_anon + memory.inactive_anon) / 1024; |
327 EXPERIMENT_HISTOGRAM_MEGABYTES("Tabs.Discard.MemAnonymousMB", | 359 EXPERIMENT_HISTOGRAM_MEGABYTES("Tabs.Discard.MemAnonymousMB", |
328 mem_anonymous_mb); | 360 mem_anonymous_mb); |
329 | 361 |
| 362 // Record graphics GEM object size in a histogram with 50 MB buckets. |
| 363 int mem_graphics_gem_mb = 0; |
| 364 if (memory.gem_size != -1) |
| 365 mem_graphics_gem_mb = memory.gem_size / 1024 / 1024; |
| 366 RecordLinearHistogram( |
| 367 "Tabs.Discard.MemGraphicsMB", mem_graphics_gem_mb, 2500, 50); |
| 368 |
| 369 // Record shared memory (used by renderer/GPU buffers). |
| 370 int mem_shmem_mb = memory.shmem / 1024; |
| 371 RecordLinearHistogram("Tabs.Discard.MemShmemMB", mem_shmem_mb, 2500, 50); |
| 372 |
330 // On Intel, graphics objects are in anonymous pages, but on ARM they are | 373 // On Intel, graphics objects are in anonymous pages, but on ARM they are |
331 // not. For a total "allocated count" add in graphics pages on ARM. | 374 // not. For a total "allocated count" add in graphics pages on ARM. |
332 int mem_allocated_mb = mem_anonymous_mb; | 375 int mem_allocated_mb = mem_anonymous_mb; |
333 #if defined(ARCH_CPU_ARM_FAMILY) | 376 #if defined(ARCH_CPU_ARM_FAMILY) |
334 if (memory.gem_size != -1) | 377 mem_allocated_mb += mem_graphics_gem_mb; |
335 mem_allocated_mb += memory.gem_size / 1024 / 1024; | |
336 #endif | 378 #endif |
337 EXPERIMENT_CUSTOM_COUNTS("Tabs.Discard.MemAllocatedMB", mem_allocated_mb, | 379 EXPERIMENT_CUSTOM_COUNTS("Tabs.Discard.MemAllocatedMB", mem_allocated_mb, |
338 256, 32768, 50) | 380 256, 32768, 50) |
339 | 381 |
340 int mem_available_mb = | 382 int mem_available_mb = |
341 (memory.active_file + memory.inactive_file + memory.free) / 1024; | 383 (memory.active_file + memory.inactive_file + memory.free) / 1024; |
342 EXPERIMENT_HISTOGRAM_MEGABYTES("Tabs.Discard.MemAvailableMB", | 384 EXPERIMENT_HISTOGRAM_MEGABYTES("Tabs.Discard.MemAvailableMB", |
343 mem_available_mb); | 385 mem_available_mb); |
344 } | 386 } |
345 // Set up to record the next interval. | 387 // Set up to record the next interval. |
346 last_discard_time_ = TimeTicks::Now(); | 388 last_discard_time_ = TimeTicks::Now(); |
347 } | 389 } |
348 | 390 |
| 391 void OomPriorityManager::RecordRecentTabDiscard() { |
| 392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 393 // If we change the interval we need to change the histogram name. |
| 394 UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute", |
| 395 recent_tab_discard_); |
| 396 // Reset for the next interval. |
| 397 recent_tab_discard_ = false; |
| 398 } |
| 399 |
349 void OomPriorityManager::PurgeBrowserMemory() { | 400 void OomPriorityManager::PurgeBrowserMemory() { |
350 // Based on experimental evidence, attempts to free memory from renderers | 401 // Based on experimental evidence, attempts to free memory from renderers |
351 // have been too slow to use in OOM situations (V8 garbage collection) or | 402 // have been too slow to use in OOM situations (V8 garbage collection) or |
352 // do not lead to persistent decreased usage (image/bitmap caches). This | 403 // do not lead to persistent decreased usage (image/bitmap caches). This |
353 // function therefore only targets large blocks of memory in the browser. | 404 // function therefore only targets large blocks of memory in the browser. |
354 for (TabContentsIterator it; !it.done(); it.Next()) { | 405 for (TabContentsIterator it; !it.done(); it.Next()) { |
355 WebContents* web_contents = *it; | 406 WebContents* web_contents = *it; |
356 // Screenshots can consume ~5 MB per web contents for platforms that do | 407 // Screenshots can consume ~5 MB per web contents for platforms that do |
357 // touch back/forward. | 408 // touch back/forward. |
358 web_contents->GetController().ClearAllScreenshots(); | 409 web_contents->GetController().ClearAllScreenshots(); |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( | 632 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( |
582 iterator->renderer_handle, score); | 633 iterator->renderer_handle, score); |
583 pid_to_oom_score_[iterator->renderer_handle] = score; | 634 pid_to_oom_score_[iterator->renderer_handle] = score; |
584 } | 635 } |
585 priority += priority_increment; | 636 priority += priority_increment; |
586 } | 637 } |
587 } | 638 } |
588 } | 639 } |
589 | 640 |
590 } // namespace chromeos | 641 } // namespace chromeos |
OLD | NEW |