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

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

Issue 2711093002: Purge once random minutes(between 30min and 60min) after backgrounded. (Closed)
Patch Set: s/RUNNING/NOT_PURGED/g Created 3 years, 10 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/tab_manager.h" 5 #include "chrome/browser/memory/tab_manager.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <set> 10 #include <set>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/bind_helpers.h" 14 #include "base/bind_helpers.h"
15 #include "base/command_line.h" 15 #include "base/command_line.h"
16 #include "base/feature_list.h" 16 #include "base/feature_list.h"
17 #include "base/macros.h" 17 #include "base/macros.h"
18 #include "base/memory/memory_pressure_monitor.h" 18 #include "base/memory/memory_pressure_monitor.h"
19 #include "base/metrics/field_trial.h" 19 #include "base/metrics/field_trial.h"
20 #include "base/metrics/histogram_macros.h" 20 #include "base/metrics/histogram_macros.h"
21 #include "base/observer_list.h" 21 #include "base/observer_list.h"
22 #include "base/process/process.h" 22 #include "base/process/process.h"
23 #include "base/rand_util.h"
23 #include "base/strings/string16.h" 24 #include "base/strings/string16.h"
24 #include "base/strings/string_number_conversions.h" 25 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_util.h" 26 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h" 27 #include "base/strings/utf_string_conversions.h"
27 #include "base/threading/thread.h" 28 #include "base/threading/thread.h"
28 #include "base/threading/thread_task_runner_handle.h" 29 #include "base/threading/thread_task_runner_handle.h"
29 #include "base/time/tick_clock.h" 30 #include "base/time/tick_clock.h"
30 #include "build/build_config.h" 31 #include "build/build_config.h"
31 #include "chrome/browser/browser_process.h" 32 #include "chrome/browser/browser_process.h"
32 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" 33 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 // For each period of this length record a statistic to indicate whether or not 78 // For each period of this length record a statistic to indicate whether or not
78 // the user experienced a low memory event. If this interval is changed, 79 // the user experienced a low memory event. If this interval is changed,
79 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic. 80 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic.
80 const int kRecentTabDiscardIntervalSeconds = 60; 81 const int kRecentTabDiscardIntervalSeconds = 60;
81 #endif 82 #endif
82 83
83 // If there has been no priority adjustment in this interval, assume the 84 // If there has been no priority adjustment in this interval, assume the
84 // machine was suspended and correct the timing statistics. 85 // machine was suspended and correct the timing statistics.
85 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; 86 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4;
86 87
87 // A suspended renderer is suspended for this duration. 88 // Within the following range after the tab is backgrounded and
88 constexpr base::TimeDelta kDurationOfRendererSuspension = 89 // time_to_first_purge_ passes, purge the tab's cache if the tab is still
89 base::TimeDelta::FromSeconds(1200); 90 // backgrounded and in-active.
Wez 2017/02/27 22:20:55 This comment is pretty confusingly worded! Would
tasak 2017/02/28 09:45:37 Done.
90 91 const unsigned int kRangeOfTimeToFirstPurgeInMinutes = 30;
chrisha 2017/02/27 13:31:06 How is this a range? Or is it implicitly lower bou
tasak 2017/02/28 09:45:37 Done.
91 // A resumed renderer is resumed for this duration.
92 constexpr base::TimeDelta kDurationOfRendererResumption =
93 base::TimeDelta::FromSeconds(10);
94 92
95 // The time during which a tab is protected from discarding after it stops being 93 // The time during which a tab is protected from discarding after it stops being
96 // audible. 94 // audible.
97 const int kAudioProtectionTimeSeconds = 60; 95 const int kAudioProtectionTimeSeconds = 60;
98 96
99 int FindTabStripModelById(int64_t target_web_contents_id, 97 int FindTabStripModelById(int64_t target_web_contents_id,
100 TabStripModel** model) { 98 TabStripModel** model) {
101 DCHECK(model); 99 DCHECK(model);
102 for (auto* browser : *BrowserList::GetInstance()) { 100 for (auto* browser : *BrowserList::GetInstance()) {
103 TabStripModel* local_model = browser->tab_strip_model(); 101 TabStripModel* local_model = browser->tab_strip_model();
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 } 222 }
225 #endif 223 #endif
226 // purge-and-suspend param is used for Purge+Suspend finch experiment 224 // purge-and-suspend param is used for Purge+Suspend finch experiment
227 // in the following way: 225 // in the following way:
228 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing 226 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing
229 std::string purge_and_suspend_time = variations::GetVariationParamValue( 227 std::string purge_and_suspend_time = variations::GetVariationParamValue(
230 "PurgeAndSuspend", "purge-and-suspend-time"); 228 "PurgeAndSuspend", "purge-and-suspend-time");
231 unsigned int time_to_first_purge_sec = 0; 229 unsigned int time_to_first_purge_sec = 0;
232 if (purge_and_suspend_time.empty() || 230 if (purge_and_suspend_time.empty() ||
233 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) 231 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec))
234 time_to_first_suspension_ = kDefaultTimeToFirstPurge; 232 time_to_first_purge_ = kDefaultTimeToFirstPurge;
235 else 233 else
236 time_to_first_suspension_ = 234 time_to_first_purge_ =
237 base::TimeDelta::FromSeconds(time_to_first_purge_sec); 235 base::TimeDelta::FromSeconds(time_to_first_purge_sec);
238 } 236 }
239 237
240 void TabManager::Stop() { 238 void TabManager::Stop() {
241 update_timer_.Stop(); 239 update_timer_.Stop();
242 recent_tab_discard_timer_.Stop(); 240 recent_tab_discard_timer_.Stop();
243 memory_pressure_listener_.reset(); 241 memory_pressure_listener_.reset();
244 } 242 }
245 243
246 TabStatsList TabManager::GetTabStats() const { 244 TabStatsList TabManager::GetTabStats() const {
(...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after
680 stats.title = contents->GetTitle(); 678 stats.title = contents->GetTitle();
681 stats.tab_contents_id = IdFromWebContents(contents); 679 stats.tab_contents_id = IdFromWebContents(contents);
682 stats_list->push_back(stats); 680 stats_list->push_back(stats);
683 } 681 }
684 } 682 }
685 } 683 }
686 684
687 // This function is called when |update_timer_| fires. It will adjust the clock 685 // This function is called when |update_timer_| fires. It will adjust the clock
688 // if needed (if it detects that the machine was asleep) and will fire the stats 686 // if needed (if it detects that the machine was asleep) and will fire the stats
689 // updating on ChromeOS via the delegate. This function also tries to purge 687 // updating on ChromeOS via the delegate. This function also tries to purge
690 // cache memory and suspend tabs which becomes and keeps backgrounded for a 688 // cache memory and suspend tabs which becomes and keeps backgrounded for a
Wez 2017/02/27 22:20:55 nit: Looks like this comment regarding purging is
tasak 2017/02/28 09:45:37 Done.
691 // while. 689 // while.
692 void TabManager::UpdateTimerCallback() { 690 void TabManager::UpdateTimerCallback() {
693 // If Chrome is shutting down, do not do anything. 691 // If Chrome is shutting down, do not do anything.
694 if (g_browser_process->IsShuttingDown()) 692 if (g_browser_process->IsShuttingDown())
695 return; 693 return;
696 694
697 if (BrowserList::GetInstance()->empty()) 695 if (BrowserList::GetInstance()->empty())
698 return; 696 return;
699 697
700 // Check for a discontinuity in time caused by the machine being suspended. 698 // Check for a discontinuity in time caused by the machine being suspended.
701 if (!last_adjust_time_.is_null()) { 699 if (!last_adjust_time_.is_null()) {
702 TimeDelta suspend_time = NowTicks() - last_adjust_time_; 700 TimeDelta suspend_time = NowTicks() - last_adjust_time_;
703 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { 701 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) {
704 // System was probably suspended, move the event timers forward in time so 702 // System was probably suspended, move the event timers forward in time so
705 // when they get subtracted out later, "uptime" is being counted. 703 // when they get subtracted out later, "uptime" is being counted.
706 start_time_ += suspend_time; 704 start_time_ += suspend_time;
707 if (!last_discard_time_.is_null()) 705 if (!last_discard_time_.is_null())
708 last_discard_time_ += suspend_time; 706 last_discard_time_ += suspend_time;
709 } 707 }
tasak 2017/02/28 09:45:37 Now we don't suspend any renderers. So we can remo
710 } 708 }
711 last_adjust_time_ = NowTicks(); 709 last_adjust_time_ = NowTicks();
712 710
713 #if defined(OS_CHROMEOS) 711 #if defined(OS_CHROMEOS)
714 TabStatsList stats_list = GetTabStats(); 712 TabStatsList stats_list = GetTabStats();
715 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. 713 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj.
716 delegate_->AdjustOomPriorities(stats_list); 714 delegate_->AdjustOomPriorities(stats_list);
717 #endif 715 #endif
718 716
719 PurgeAndSuspendBackgroundedTabs(); 717 PurgeBackgroundedTabs();
720 } 718 }
721 719
722 TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState( 720 TabManager::PurgeState TabManager::GetNextPurgeState(
723 content::WebContents* content, 721 content::WebContents* content,
724 base::TimeTicks current_time, 722 base::TimeTicks current_time) const {
725 const base::TimeDelta& time_to_first_suspension) const {
726 DCHECK(content); 723 DCHECK(content);
Wez 2017/02/27 22:20:55 You don't need a DCHECK here, since you're about t
tasak 2017/02/28 09:45:37 Done.
727 PurgeAndSuspendState state = 724 PurgeState state = GetWebContentsData(content)->GetPurgeState();
728 GetWebContentsData(content)->GetPurgeAndSuspendState(); 725 if (state == PURGED)
726 return state;
729 727
730 auto time_passed = current_time - 728 DCHECK(state == NOT_PURGED);
Wez 2017/02/27 22:20:55 This is a tautologous DCHECK, since there are only
tasak 2017/02/28 09:45:37 Done.
731 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); 729 auto time_passed =
Wez 2017/02/27 22:20:55 There's no need to use |auto| here; it makes the c
tasak 2017/02/28 09:45:37 Done.
732 switch (state) { 730 current_time - GetWebContentsData(content)->LastInactiveTime();
733 case RUNNING: 731 if (time_passed > GetWebContentsData(content)->TimeToFirstPurge())
734 if (time_passed > time_to_first_suspension) 732 return PURGED;
735 return SUSPENDED;
736 break;
737 case RESUMED:
738 if (time_passed > kDurationOfRendererResumption)
739 return SUSPENDED;
740 break;
741 case SUSPENDED:
742 if (time_passed > kDurationOfRendererSuspension)
743 return RESUMED;
744 break;
745 }
746 return state; 733 return state;
747 } 734 }
748 735
749 void TabManager::PurgeAndSuspendBackgroundedTabs() { 736 void TabManager::PurgeBackgroundedTabs() {
750 base::TimeTicks current_time = NowTicks(); 737 base::TimeTicks current_time = NowTicks();
751 auto tab_stats = GetUnsortedTabStats(); 738 auto tab_stats = GetUnsortedTabStats();
752 for (auto& tab : tab_stats) { 739 for (auto& tab : tab_stats) {
753 if (!tab.render_process_host->IsProcessBackgrounded()) 740 if (!tab.render_process_host->IsProcessBackgrounded())
754 continue; 741 continue;
755 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) 742 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id))
756 continue; 743 continue;
757 744
758 WebContents* content = GetWebContentsById(tab.tab_contents_id); 745 WebContents* content = GetWebContentsById(tab.tab_contents_id);
759 if (!content) 746 if (!content)
760 continue; 747 continue;
761 748
762 PurgeAndSuspendState current_state = 749 PurgeState current_state = GetWebContentsData(content)->GetPurgeState();
763 GetWebContentsData(content)->GetPurgeAndSuspendState(); 750 PurgeState next_state = GetNextPurgeState(content, current_time);
764 // If the tab's purge-and-suspend state is not RUNNING, the tab should be
765 // backgrounded. Since tab.last_hidden is updated everytime the tab is
766 // hidden, we should see tab.last_hidden < last_modified_time.
767 DCHECK(current_state == RUNNING ||
768 tab.last_hidden <
769 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime());
770 PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState(
771 content, current_time, time_to_first_suspension_);
772 if (current_state == next_state) 751 if (current_state == next_state)
773 continue; 752 continue;
774 753
775 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without 754 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without
776 // timers for simplicity, so PurgeAndSuspend is called even after the 755 // timers for simplicity, so PurgeAndSuspend is called even after the
777 // renderer is purged and suspended once. This should be replaced with 756 // renderer is purged and suspended once. This should be replaced with
778 // timers if we want necessary and sufficient signals. 757 // timers if we want necessary and sufficient signals.
Wez 2017/02/27 22:20:55 It's not clear what this (pre-existing) comment is
tasak 2017/02/28 09:45:37 Done.
779 GetWebContentsData(content)->SetPurgeAndSuspendState(next_state); 758 GetWebContentsData(content)->SetPurgeState(next_state);
780 switch (next_state) { 759 DCHECK(next_state == PURGED);
Wez 2017/02/27 22:20:55 As noted in the previous function, you can replace
tasak 2017/02/28 09:45:37 Done.
781 case SUSPENDED: 760 // TODO(tasak): rename PurgeAndSuspend with a better name, e.g.
782 tab.render_process_host->PurgeAndSuspend(); 761 // RequestPurgeCache, because we don't suspend any renderers.
783 break; 762 tab.render_process_host->PurgeAndSuspend();
784 case RESUMED:
785 tab.render_process_host->Resume();
786 break;
787 case RUNNING:
788 NOTREACHED();
789 }
790 } 763 }
791 } 764 }
792 765
793 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { 766 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) {
794 // Can't discard active index. 767 // Can't discard active index.
795 if (model->active_index() == index) 768 if (model->active_index() == index)
796 return nullptr; 769 return nullptr;
797 770
798 WebContents* old_contents = model->GetWebContentsAt(index); 771 WebContents* old_contents = model->GetWebContentsAt(index);
799 772
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
880 data->SetRecentlyAudible(current_state); 853 data->SetRecentlyAudible(current_state);
881 data->SetLastAudioChangeTime(NowTicks()); 854 data->SetLastAudioChangeTime(NowTicks());
882 } 855 }
883 } 856 }
884 857
885 void TabManager::ActiveTabChanged(content::WebContents* old_contents, 858 void TabManager::ActiveTabChanged(content::WebContents* old_contents,
886 content::WebContents* new_contents, 859 content::WebContents* new_contents,
887 int index, 860 int index,
888 int reason) { 861 int reason) {
889 GetWebContentsData(new_contents)->SetDiscardState(false); 862 GetWebContentsData(new_contents)->SetDiscardState(false);
890 GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); 863 // To avoid purging many processes' cache at the same time, we will use
864 // time_to_first_purge_ + random value with in the range
865 // kRangeOfTimeToFirstPurgeInMinutes to invoke purge.
Wez 2017/02/27 22:20:55 I think the more important thing to get across in
tasak 2017/02/28 09:45:37 Moved the code into the statement: if(old_contents
866 base::TimeDelta time_to_first_purge =
867 time_to_first_purge_ +
868 base::TimeDelta::FromMinutes(
869 base::RandGenerator(kRangeOfTimeToFirstPurgeInMinutes));
870 GetWebContentsData(new_contents)->SetTimeToFirstPurge(time_to_first_purge);
Wez 2017/02/27 22:20:55 nit: I'd suggest calling this SetTimeToPurge(), so
tasak 2017/02/28 09:45:37 Done.
871 GetWebContentsData(new_contents)->SetPurgeState(NOT_PURGED);
891 // If |old_contents| is set, that tab has switched from being active to 872 // If |old_contents| is set, that tab has switched from being active to
892 // inactive, so record the time of that transition. 873 // inactive, so record the time of that transition.
893 if (old_contents) 874 if (old_contents)
894 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); 875 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks());
Wez 2017/02/27 22:20:55 Again, this is pre-existing code, but shouldn't th
tasak 2017/02/28 09:45:37 As far as I understand, new_contents is a tab whic
895 } 876 }
896 877
897 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, 878 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model,
898 content::WebContents* contents, 879 content::WebContents* contents,
899 int index, 880 int index,
900 bool foreground) { 881 bool foreground) {
901 // Only interested in background tabs, as foreground tabs get taken care of by 882 // Only interested in background tabs, as foreground tabs get taken care of by
902 // ActiveTabChanged. 883 // ActiveTabChanged.
903 if (foreground) 884 if (foreground)
904 return; 885 return;
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
1032 // platform. 1013 // platform.
1033 std::string allow_multiple_discards = variations::GetVariationParamValue( 1014 std::string allow_multiple_discards = variations::GetVariationParamValue(
1034 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); 1015 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards");
1035 return (allow_multiple_discards != "true"); 1016 return (allow_multiple_discards != "true");
1036 #else 1017 #else
1037 return false; 1018 return false;
1038 #endif 1019 #endif
1039 } 1020 }
1040 1021
1041 } // namespace memory 1022 } // namespace memory
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698