OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "content/browser/memory/memory_coordinator_impl.h" | 5 #include "content/browser/memory/memory_coordinator_impl.h" |
6 | 6 |
7 #include "base/threading/thread_task_runner_handle.h" | 7 #include "base/threading/thread_task_runner_handle.h" |
8 #include "base/trace_event/trace_event.h" | 8 #include "base/trace_event/trace_event.h" |
9 #include "content/browser/memory/memory_monitor.h" | 9 #include "content/browser/memory/memory_monitor.h" |
10 #include "content/public/browser/notification_service.h" | 10 #include "content/public/browser/notification_service.h" |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
69 return nullptr; | 69 return nullptr; |
70 return base::Singleton<MemoryCoordinator, | 70 return base::Singleton<MemoryCoordinator, |
71 MemoryCoordinatorSingletonTraits>::get(); | 71 MemoryCoordinatorSingletonTraits>::get(); |
72 } | 72 } |
73 | 73 |
74 MemoryCoordinatorImpl::MemoryCoordinatorImpl( | 74 MemoryCoordinatorImpl::MemoryCoordinatorImpl( |
75 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | 75 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
76 std::unique_ptr<MemoryMonitor> memory_monitor) | 76 std::unique_ptr<MemoryMonitor> memory_monitor) |
77 : task_runner_(task_runner), | 77 : task_runner_(task_runner), |
78 memory_monitor_(std::move(memory_monitor)), | 78 memory_monitor_(std::move(memory_monitor)), |
79 remaining_global_budget_mb_(0), | |
79 weak_ptr_factory_(this) { | 80 weak_ptr_factory_(this) { |
80 DCHECK(memory_monitor_.get()); | 81 DCHECK(memory_monitor_.get()); |
81 update_state_callback_ = base::Bind(&MemoryCoordinatorImpl::UpdateState, | 82 update_state_callback_ = base::Bind(&MemoryCoordinatorImpl::UpdateState, |
82 weak_ptr_factory_.GetWeakPtr()); | 83 weak_ptr_factory_.GetWeakPtr()); |
83 | 84 |
84 // Set initial parameters for calculating the global state. | 85 // Set initial parameters for calculating the global state. |
85 expected_renderer_size_ = kDefaultExpectedRendererSizeMB; | 86 expected_renderer_size_ = kDefaultExpectedRendererSizeMB; |
Hannes Payer (out of office)
2016/11/11 12:55:19
Unrelated to this CL: How did you come up with kDe
bashi
2016/11/12 01:30:53
From UMA (median of average). This is just an esti
| |
86 new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled; | 87 new_renderers_until_throttled_ = kDefaultNewRenderersUntilThrottled; |
87 new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended; | 88 new_renderers_until_suspended_ = kDefaultNewRenderersUntilSuspended; |
88 new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal; | 89 new_renderers_back_to_normal_ = kDefaultNewRenderersBackToNormal; |
89 new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled; | 90 new_renderers_back_to_throttled_ = kDefaultNewRenderersBackToThrottled; |
90 minimum_transition_period_ = | 91 minimum_transition_period_ = |
91 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds); | 92 base::TimeDelta::FromSeconds(kDefaultMinimumTransitionPeriodSeconds); |
92 monitoring_interval_ = | 93 monitoring_interval_ = |
93 base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds); | 94 base::TimeDelta::FromSeconds(kDefaultMonitoringIntervalSeconds); |
94 } | 95 } |
95 | 96 |
(...skipping 29 matching lines...) Expand all Loading... | |
125 // updated later by the task posted at ScheduleUpdateState. | 126 // updated later by the task posted at ScheduleUpdateState. |
126 DCHECK(memory_state != MemoryState::UNKNOWN); | 127 DCHECK(memory_state != MemoryState::UNKNOWN); |
127 base::MemoryState prev_state = current_state_; | 128 base::MemoryState prev_state = current_state_; |
128 current_state_ = memory_state; | 129 current_state_ = memory_state; |
129 if (prev_state != current_state_) { | 130 if (prev_state != current_state_) { |
130 NotifyStateToClients(); | 131 NotifyStateToClients(); |
131 NotifyStateToChildren(); | 132 NotifyStateToChildren(); |
132 } | 133 } |
133 } | 134 } |
134 | 135 |
136 int64_t MemoryCoordinatorImpl::GetRemainingGlobalBudget() const { | |
137 return remaining_global_budget_mb_; | |
138 } | |
139 | |
135 void MemoryCoordinatorImpl::Observe(int type, | 140 void MemoryCoordinatorImpl::Observe(int type, |
136 const NotificationSource& source, | 141 const NotificationSource& source, |
137 const NotificationDetails& details) { | 142 const NotificationDetails& details) { |
138 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); | 143 DCHECK(type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); |
139 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); | 144 RenderWidgetHost* render_widget_host = Source<RenderWidgetHost>(source).ptr(); |
140 RenderProcessHost* process = render_widget_host->GetProcess(); | 145 RenderProcessHost* process = render_widget_host->GetProcess(); |
141 if (!process) | 146 if (!process) |
142 return; | 147 return; |
143 auto iter = children().find(process->GetID()); | 148 auto iter = children().find(process->GetID()); |
144 if (iter == children().end()) | 149 if (iter == children().end()) |
145 return; | 150 return; |
146 bool is_visible = *Details<bool>(details).ptr(); | 151 bool is_visible = *Details<bool>(details).ptr(); |
147 // We don't throttle/suspend a visible renderer for now. | 152 // We don't throttle/suspend a visible renderer for now. |
148 auto new_state = is_visible ? mojom::MemoryState::NORMAL | 153 auto new_state = is_visible ? mojom::MemoryState::NORMAL |
149 : ToMojomMemoryState(current_state_); | 154 : ToMojomMemoryState(current_state_); |
150 SetMemoryState(iter->first, new_state); | 155 SetMemoryState(iter->first, new_state); |
151 } | 156 } |
152 | 157 |
153 base::MemoryState MemoryCoordinatorImpl::CalculateNextState() { | 158 base::MemoryState MemoryCoordinatorImpl::CalculateNextState( |
159 int free_memory_until_critical_mb) { | |
154 using MemoryState = base::MemoryState; | 160 using MemoryState = base::MemoryState; |
155 | 161 |
156 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB(); | 162 // If memory is already critical then immediately go to the most aggressive |
157 if (available <= 0) | 163 // state. |
164 if (free_memory_until_critical_mb <= 0) | |
158 return MemoryState::SUSPENDED; | 165 return MemoryState::SUSPENDED; |
159 | 166 |
160 int expected_renderer_count = available / expected_renderer_size_; | 167 // Determine the next state. |
161 | 168 int expected_renderer_count = |
169 free_memory_until_critical_mb / expected_renderer_size_; | |
162 switch (current_state_) { | 170 switch (current_state_) { |
163 case MemoryState::NORMAL: | 171 case MemoryState::NORMAL: |
164 if (expected_renderer_count <= new_renderers_until_suspended_) | 172 if (expected_renderer_count <= new_renderers_until_suspended_) |
165 return MemoryState::SUSPENDED; | 173 return MemoryState::SUSPENDED; |
166 if (expected_renderer_count <= new_renderers_until_throttled_) | 174 if (expected_renderer_count <= new_renderers_until_throttled_) |
167 return MemoryState::THROTTLED; | 175 return MemoryState::THROTTLED; |
168 return MemoryState::NORMAL; | 176 return MemoryState::NORMAL; |
169 case MemoryState::THROTTLED: | 177 case MemoryState::THROTTLED: |
170 if (expected_renderer_count <= new_renderers_until_suspended_) | 178 if (expected_renderer_count <= new_renderers_until_suspended_) |
171 return MemoryState::SUSPENDED; | 179 return MemoryState::SUSPENDED; |
172 if (expected_renderer_count >= new_renderers_back_to_normal_) | 180 if (expected_renderer_count >= new_renderers_back_to_normal_) |
173 return MemoryState::NORMAL; | 181 return MemoryState::NORMAL; |
174 return MemoryState::THROTTLED; | 182 return MemoryState::THROTTLED; |
175 case MemoryState::SUSPENDED: | 183 case MemoryState::SUSPENDED: |
176 if (expected_renderer_count >= new_renderers_back_to_normal_) | 184 if (expected_renderer_count >= new_renderers_back_to_normal_) |
177 return MemoryState::NORMAL; | 185 return MemoryState::NORMAL; |
178 if (expected_renderer_count >= new_renderers_back_to_throttled_) | 186 if (expected_renderer_count >= new_renderers_back_to_throttled_) |
179 return MemoryState::THROTTLED; | 187 return MemoryState::THROTTLED; |
180 return MemoryState::SUSPENDED; | 188 return MemoryState::SUSPENDED; |
181 case MemoryState::UNKNOWN: | 189 case MemoryState::UNKNOWN: |
182 // Fall through | 190 // Fall through. |
183 default: | 191 default: |
184 NOTREACHED(); | 192 NOTREACHED(); |
185 return MemoryState::UNKNOWN; | 193 return MemoryState::UNKNOWN; |
186 } | 194 } |
187 } | 195 } |
188 | 196 |
197 int64_t MemoryCoordinatorImpl::CalculateRemainingGlobalBudget( | |
198 int free_memory_until_critical_mb) { | |
199 int renderer_count = new_renderers_until_throttled_; | |
Hannes Payer (out of office)
2016/11/11 12:55:19
I do not understand why you use kDefaultNewRendere
bashi
2016/11/12 01:30:53
Because we don't know what an appropriate value fo
| |
200 if (current_state_ != MemoryState::NORMAL) | |
201 renderer_count = new_renderers_back_to_normal_; | |
haraken
2016/11/05 12:52:21
Shouldn't new_renderers_back_to_normal_ be always
bashi
2016/11/07 00:16:37
We have different parameters for # of renderers to
chrisha
2016/11/07 19:33:41
Yeah, this enables a "dead band" to prevent thrash
| |
202 | |
203 return free_memory_until_critical_mb - | |
204 renderer_count * expected_renderer_size_; | |
205 } | |
206 | |
189 void MemoryCoordinatorImpl::UpdateState() { | 207 void MemoryCoordinatorImpl::UpdateState() { |
190 base::TimeTicks now = base::TimeTicks::Now(); | 208 base::TimeTicks now = base::TimeTicks::Now(); |
209 | |
210 int available = memory_monitor_->GetFreeMemoryUntilCriticalMB(); | |
211 | |
191 MemoryState prev_state = current_state_; | 212 MemoryState prev_state = current_state_; |
192 MemoryState next_state = CalculateNextState(); | 213 MemoryState next_state = CalculateNextState(available); |
193 | 214 |
194 if (last_state_change_.is_null() || current_state_ != next_state) { | 215 if (last_state_change_.is_null() || current_state_ != next_state) { |
195 current_state_ = next_state; | 216 current_state_ = next_state; |
196 last_state_change_ = now; | 217 last_state_change_ = now; |
197 } | 218 } |
198 | 219 |
199 if (next_state != prev_state) { | 220 if (next_state != prev_state) { |
200 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::UpdateState", | 221 TRACE_EVENT2("memory-infra", "MemoryCoordinatorImpl::UpdateState", |
201 "prev", MemoryStateToString(prev_state), | 222 "prev", MemoryStateToString(prev_state), |
202 "next", MemoryStateToString(next_state)); | 223 "next", MemoryStateToString(next_state)); |
203 | 224 |
204 NotifyStateToClients(); | 225 NotifyStateToClients(); |
205 NotifyStateToChildren(); | 226 NotifyStateToChildren(); |
206 ScheduleUpdateState(minimum_transition_period_); | 227 ScheduleUpdateState(minimum_transition_period_); |
207 } else { | 228 } else { |
208 ScheduleUpdateState(monitoring_interval_); | 229 ScheduleUpdateState(monitoring_interval_); |
209 } | 230 } |
231 | |
232 // Update the global budget. This is used by some passive clients of the | |
233 // memory coordinator to smoothly scale their own resource consumption in | |
234 // sync with the memory coordinator. | |
235 int64_t prev_remaining = remaining_global_budget_mb_; | |
236 int64_t next_remaining = CalculateRemainingGlobalBudget(available); | |
237 if (prev_remaining != next_remaining) { | |
bashi
2016/11/07 00:16:37
How likely does this condition become true? |free_
chrisha
2016/11/07 19:33:41
Very unlikely to remain true, but this is a cheap
bashi
2016/11/07 23:17:08
Acknowledged.
chrisha
2016/12/15 21:10:24
(This is a moot point with a "pull" model.)
| |
238 remaining_global_budget_mb_ = next_remaining; | |
239 NotifyRemainingGlobalBudgetToChildren(); | |
haraken
2016/11/05 12:52:21
How frequently will this IPC be called?
bashi
2016/11/07 00:16:37
My guess is that it's almost the same as how frequ
chrisha
2016/11/07 19:33:41
Yup. Although we should likely still continue to c
bashi
2016/11/07 23:17:08
Yeah, as haraken@ said, async API may not be usefu
Hannes Payer (out of office)
2016/11/11 12:55:19
Currently, I am only planning to take advantage of
chrisha
2016/12/15 21:10:24
Moved to a "pull" model.
| |
240 } | |
210 } | 241 } |
211 | 242 |
212 void MemoryCoordinatorImpl::NotifyStateToClients() { | 243 void MemoryCoordinatorImpl::NotifyStateToClients() { |
213 auto state = GetCurrentMemoryState(); | 244 auto state = GetCurrentMemoryState(); |
214 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); | 245 base::MemoryCoordinatorClientRegistry::GetInstance()->Notify(state); |
215 } | 246 } |
216 | 247 |
217 void MemoryCoordinatorImpl::NotifyStateToChildren() { | 248 void MemoryCoordinatorImpl::NotifyStateToChildren() { |
218 auto mojo_state = ToMojomMemoryState(current_state_); | 249 auto mojo_state = ToMojomMemoryState(current_state_); |
219 // It's OK to call SetMemoryState() unconditionally because it checks whether | 250 // It's OK to call SetMemoryState() unconditionally because it checks whether |
220 // this state transition is valid. | 251 // this state transition is valid. |
221 for (auto& iter : children()) | 252 for (auto& iter : children()) |
222 SetMemoryState(iter.first, mojo_state); | 253 SetMemoryState(iter.first, mojo_state); |
223 } | 254 } |
224 | 255 |
256 void MemoryCoordinatorImpl::NotifyRemainingGlobalBudgetToChildren() { | |
257 for (auto& iter : children()) | |
258 SetRemainingGlobalBudget(iter.first, remaining_global_budget_mb_); | |
259 } | |
260 | |
225 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) { | 261 void MemoryCoordinatorImpl::ScheduleUpdateState(base::TimeDelta delta) { |
226 task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta); | 262 task_runner_->PostDelayedTask(FROM_HERE, update_state_callback_, delta); |
227 } | 263 } |
228 | 264 |
229 bool MemoryCoordinatorImpl::ValidateParameters() { | 265 bool MemoryCoordinatorImpl::ValidateParameters() { |
230 return (new_renderers_until_throttled_ > new_renderers_until_suspended_) && | 266 return (new_renderers_until_throttled_ > new_renderers_until_suspended_) && |
231 (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) && | 267 (new_renderers_back_to_normal_ > new_renderers_back_to_throttled_) && |
232 (new_renderers_back_to_normal_ > new_renderers_until_throttled_) && | 268 (new_renderers_back_to_normal_ > new_renderers_until_throttled_) && |
233 (new_renderers_back_to_throttled_ > new_renderers_until_suspended_); | 269 (new_renderers_back_to_throttled_ > new_renderers_until_suspended_); |
234 } | 270 } |
235 | 271 |
236 } // namespace content | 272 } // namespace content |
OLD | NEW |