system_memory_pressure_evaluator_win.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // Copyright 2019 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.
  4. #include "components/memory_pressure/system_memory_pressure_evaluator_win.h"
  5. #include <windows.h>
  6. #include <memory>
  7. #include "base/bind.h"
  8. #include "base/metrics/histogram_functions.h"
  9. #include "base/system/sys_info.h"
  10. #include "base/task/single_thread_task_runner.h"
  11. #include "base/threading/sequenced_task_runner_handle.h"
  12. #include "base/time/time.h"
  13. #include "base/win/object_watcher.h"
  14. #include "components/memory_pressure/multi_source_memory_pressure_monitor.h"
  15. namespace memory_pressure {
  16. namespace win {
  17. namespace {
  18. static const DWORDLONG kMBBytes = 1024 * 1024;
  19. // Implements ObjectWatcher::Delegate by forwarding to a provided callback.
  20. class MemoryPressureWatcherDelegate
  21. : public base::win::ObjectWatcher::Delegate {
  22. public:
  23. MemoryPressureWatcherDelegate(base::win::ScopedHandle handle,
  24. base::OnceClosure callback);
  25. ~MemoryPressureWatcherDelegate() override;
  26. MemoryPressureWatcherDelegate(const MemoryPressureWatcherDelegate& other) =
  27. delete;
  28. MemoryPressureWatcherDelegate& operator=(
  29. const MemoryPressureWatcherDelegate&) = delete;
  30. void ReplaceWatchedHandleForTesting(base::win::ScopedHandle handle);
  31. void SetCallbackForTesting(base::OnceClosure callback) {
  32. callback_ = std::move(callback);
  33. }
  34. private:
  35. void OnObjectSignaled(HANDLE handle) override;
  36. base::win::ScopedHandle handle_;
  37. base::win::ObjectWatcher watcher_;
  38. base::OnceClosure callback_;
  39. };
  40. MemoryPressureWatcherDelegate::MemoryPressureWatcherDelegate(
  41. base::win::ScopedHandle handle,
  42. base::OnceClosure callback)
  43. : handle_(std::move(handle)), callback_(std::move(callback)) {
  44. DCHECK(handle_.IsValid());
  45. CHECK(watcher_.StartWatchingOnce(handle_.Get(), this));
  46. }
  47. MemoryPressureWatcherDelegate::~MemoryPressureWatcherDelegate() = default;
  48. void MemoryPressureWatcherDelegate::ReplaceWatchedHandleForTesting(
  49. base::win::ScopedHandle handle) {
  50. if (watcher_.IsWatching())
  51. watcher_.StopWatching();
  52. handle_ = std::move(handle);
  53. CHECK(watcher_.StartWatchingOnce(handle_.Get(), this));
  54. }
  55. void MemoryPressureWatcherDelegate::OnObjectSignaled(HANDLE handle) {
  56. DCHECK_EQ(handle, handle_.Get());
  57. std::move(callback_).Run();
  58. }
  59. } // namespace
  60. // Check the amount of RAM left every 5 seconds.
  61. const base::TimeDelta SystemMemoryPressureEvaluator::kMemorySamplingPeriod =
  62. base::Seconds(5);
  63. // The following constants have been lifted from similar values in the ChromeOS
  64. // memory pressure monitor. The values were determined experimentally to ensure
  65. // sufficient responsiveness of the memory pressure subsystem, and minimal
  66. // overhead.
  67. const base::TimeDelta SystemMemoryPressureEvaluator::kModeratePressureCooldown =
  68. base::Seconds(10);
  69. // TODO(chrisha): Explore the following constants further with an experiment.
  70. // A system is considered 'high memory' if it has more than 1.5GB of system
  71. // memory available for use by the memory manager (not reserved for hardware
  72. // and drivers). This is a fuzzy version of the ~2GB discussed below.
  73. const int SystemMemoryPressureEvaluator::kLargeMemoryThresholdMb = 1536;
  74. // These are the default thresholds used for systems with < ~2GB of physical
  75. // memory. Such systems have been observed to always maintain ~100MB of
  76. // available memory, paging until that is the case. To try to avoid paging a
  77. // threshold slightly above this is chosen. The moderate threshold is slightly
  78. // less grounded in reality and chosen as 2.5x critical.
  79. const int
  80. SystemMemoryPressureEvaluator::kSmallMemoryDefaultModerateThresholdMb = 500;
  81. const int
  82. SystemMemoryPressureEvaluator::kSmallMemoryDefaultCriticalThresholdMb = 200;
  83. // These are the default thresholds used for systems with >= ~2GB of physical
  84. // memory. Such systems have been observed to always maintain ~300MB of
  85. // available memory, paging until that is the case.
  86. const int
  87. SystemMemoryPressureEvaluator::kLargeMemoryDefaultModerateThresholdMb =
  88. 1000;
  89. const int
  90. SystemMemoryPressureEvaluator::kLargeMemoryDefaultCriticalThresholdMb = 400;
  91. // A memory pressure evaluator that receives memory pressure notifications from
  92. // the OS and forwards them to the memory pressure monitor.
  93. class SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator {
  94. public:
  95. using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
  96. explicit OSSignalsMemoryPressureEvaluator(
  97. std::unique_ptr<MemoryPressureVoter> voter);
  98. ~OSSignalsMemoryPressureEvaluator();
  99. OSSignalsMemoryPressureEvaluator(
  100. const OSSignalsMemoryPressureEvaluator& other) = delete;
  101. OSSignalsMemoryPressureEvaluator& operator=(
  102. const OSSignalsMemoryPressureEvaluator&) = delete;
  103. // Creates the watcher used to receive the low and high memory notifications.
  104. void Start();
  105. MemoryPressureWatcherDelegate* GetWatcherForTesting() const {
  106. return memory_notification_watcher_.get();
  107. }
  108. void WaitForHighMemoryNotificationForTesting(base::OnceClosure closure);
  109. private:
  110. // Called when receiving a low/high memory notification.
  111. void OnLowMemoryNotification();
  112. void OnHighMemoryNotification();
  113. void StartLowMemoryNotificationWatcher();
  114. void StartHighMemoryNotificationWatcher();
  115. // The period of the critical pressure notification timer.
  116. static constexpr base::TimeDelta kHighPressureNotificationInterval =
  117. base::Seconds(2);
  118. // The voter used to cast the votes.
  119. std::unique_ptr<MemoryPressureVoter> voter_;
  120. // The memory notification watcher.
  121. std::unique_ptr<MemoryPressureWatcherDelegate> memory_notification_watcher_;
  122. // Timer that will re-emit the critical memory pressure signal until the
  123. // memory gets high again.
  124. base::RepeatingTimer critical_pressure_notification_timer_;
  125. // Beginning of the critical memory pressure session.
  126. base::TimeTicks critical_pressure_session_begin_;
  127. // Ensures that this object is used from a single sequence.
  128. SEQUENCE_CHECKER(sequence_checker_);
  129. };
  130. SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
  131. std::unique_ptr<MemoryPressureVoter> voter)
  132. : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)),
  133. moderate_threshold_mb_(0),
  134. critical_threshold_mb_(0),
  135. moderate_pressure_repeat_count_(0) {
  136. InferThresholds();
  137. StartObserving();
  138. }
  139. SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
  140. int moderate_threshold_mb,
  141. int critical_threshold_mb,
  142. std::unique_ptr<MemoryPressureVoter> voter)
  143. : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)),
  144. moderate_threshold_mb_(moderate_threshold_mb),
  145. critical_threshold_mb_(critical_threshold_mb),
  146. moderate_pressure_repeat_count_(0) {
  147. DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
  148. DCHECK_LE(0, critical_threshold_mb_);
  149. StartObserving();
  150. }
  151. SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() {
  152. StopObserving();
  153. }
  154. void SystemMemoryPressureEvaluator::CheckMemoryPressureSoon() {
  155. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  156. base::SequencedTaskRunnerHandle::Get()->PostTask(
  157. FROM_HERE, BindOnce(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
  158. weak_ptr_factory_.GetWeakPtr()));
  159. }
  160. void SystemMemoryPressureEvaluator::CreateOSSignalPressureEvaluator(
  161. std::unique_ptr<MemoryPressureVoter> voter) {
  162. os_signals_evaluator_ =
  163. std::make_unique<OSSignalsMemoryPressureEvaluator>(std::move(voter));
  164. os_signals_evaluator_->Start();
  165. }
  166. void SystemMemoryPressureEvaluator::ReplaceWatchedHandleForTesting(
  167. base::win::ScopedHandle handle) {
  168. os_signals_evaluator_->GetWatcherForTesting()->ReplaceWatchedHandleForTesting(
  169. std::move(handle));
  170. }
  171. void SystemMemoryPressureEvaluator::WaitForHighMemoryNotificationForTesting(
  172. base::OnceClosure closure) {
  173. os_signals_evaluator_->WaitForHighMemoryNotificationForTesting(
  174. std::move(closure));
  175. }
  176. void SystemMemoryPressureEvaluator::InferThresholds() {
  177. // Default to a 'high' memory situation, which uses more conservative
  178. // thresholds.
  179. bool high_memory = true;
  180. MEMORYSTATUSEX mem_status = {};
  181. if (GetSystemMemoryStatus(&mem_status)) {
  182. static const DWORDLONG kLargeMemoryThresholdBytes =
  183. static_cast<DWORDLONG>(kLargeMemoryThresholdMb) * kMBBytes;
  184. high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes;
  185. }
  186. if (high_memory) {
  187. moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb;
  188. critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb;
  189. } else {
  190. moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb;
  191. critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb;
  192. }
  193. }
  194. void SystemMemoryPressureEvaluator::StartObserving() {
  195. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  196. timer_.Start(
  197. FROM_HERE, kMemorySamplingPeriod,
  198. BindRepeating(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
  199. weak_ptr_factory_.GetWeakPtr()));
  200. }
  201. void SystemMemoryPressureEvaluator::StopObserving() {
  202. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  203. // If StartObserving failed, StopObserving will still get called.
  204. timer_.Stop();
  205. weak_ptr_factory_.InvalidateWeakPtrs();
  206. }
  207. void SystemMemoryPressureEvaluator::CheckMemoryPressure() {
  208. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  209. // Get the previous pressure level and update the current one.
  210. MemoryPressureLevel old_vote = current_vote();
  211. SetCurrentVote(CalculateCurrentPressureLevel());
  212. // |notify| will be set to true if MemoryPressureListeners need to be
  213. // notified of a memory pressure level state change.
  214. bool notify = false;
  215. switch (current_vote()) {
  216. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
  217. break;
  218. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
  219. if (old_vote != current_vote()) {
  220. // This is a new transition to moderate pressure so notify.
  221. moderate_pressure_repeat_count_ = 0;
  222. notify = true;
  223. } else {
  224. // Already in moderate pressure, only notify if sustained over the
  225. // cooldown period.
  226. const int kModeratePressureCooldownCycles =
  227. kModeratePressureCooldown / kMemorySamplingPeriod;
  228. if (++moderate_pressure_repeat_count_ ==
  229. kModeratePressureCooldownCycles) {
  230. moderate_pressure_repeat_count_ = 0;
  231. notify = true;
  232. }
  233. }
  234. break;
  235. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
  236. // Always notify of critical pressure levels.
  237. notify = true;
  238. break;
  239. }
  240. SendCurrentVote(notify);
  241. }
  242. base::MemoryPressureListener::MemoryPressureLevel
  243. SystemMemoryPressureEvaluator::CalculateCurrentPressureLevel() {
  244. MEMORYSTATUSEX mem_status = {};
  245. if (!GetSystemMemoryStatus(&mem_status))
  246. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
  247. // How much system memory is actively available for use right now, in MBs.
  248. int phys_free = static_cast<int>(mem_status.ullAvailPhys / kMBBytes);
  249. // TODO(chrisha): This should eventually care about address space pressure,
  250. // but the browser process (where this is running) effectively never runs out
  251. // of address space. Renderers occasionally do, but it does them no good to
  252. // have the browser process monitor address space pressure. Long term,
  253. // renderers should run their own address space pressure monitors and act
  254. // accordingly, with the browser making cross-process decisions based on
  255. // system memory pressure.
  256. // Determine if the physical memory is under critical memory pressure.
  257. if (phys_free <= critical_threshold_mb_)
  258. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
  259. // Determine if the physical memory is under moderate memory pressure.
  260. if (phys_free <= moderate_threshold_mb_)
  261. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
  262. // No memory pressure was detected.
  263. return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
  264. }
  265. bool SystemMemoryPressureEvaluator::GetSystemMemoryStatus(
  266. MEMORYSTATUSEX* mem_status) {
  267. DCHECK(mem_status);
  268. mem_status->dwLength = sizeof(*mem_status);
  269. if (!::GlobalMemoryStatusEx(mem_status))
  270. return false;
  271. return true;
  272. }
  273. SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  274. OSSignalsMemoryPressureEvaluator(std::unique_ptr<MemoryPressureVoter> voter)
  275. : voter_(std::move(voter)) {}
  276. SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  277. ~OSSignalsMemoryPressureEvaluator() = default;
  278. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::Start() {
  279. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  280. // Start by observing the low memory notifications. If the system is already
  281. // under pressure this will run the |OnLowMemoryNotification| callback and
  282. // automatically switch to waiting for the high memory notification/
  283. StartLowMemoryNotificationWatcher();
  284. }
  285. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  286. OnLowMemoryNotification() {
  287. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  288. critical_pressure_session_begin_ = base::TimeTicks::Now();
  289. base::UmaHistogramEnumeration(
  290. "Discarding.WinOSPressureSignals.PressureLevelOnLowMemoryNotification",
  291. base::MemoryPressureMonitor::Get()->GetCurrentPressureLevel());
  292. base::UmaHistogramMemoryMB(
  293. "Discarding.WinOSPressureSignals."
  294. "AvailableMemoryMbOnLowMemoryNotification",
  295. base::SysInfo::AmountOfAvailablePhysicalMemory() / 1024 / 1024);
  296. voter_->SetVote(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
  297. /* notify = */ true);
  298. // Start a timer to repeat the notification at regular interval until
  299. // OnHighMemoryNotification gets called.
  300. critical_pressure_notification_timer_.Start(
  301. FROM_HERE, kHighPressureNotificationInterval,
  302. base::BindRepeating(
  303. &MemoryPressureVoter::SetVote, base::Unretained(voter_.get()),
  304. base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
  305. /* notify = */ true));
  306. // Start the high memory notification watcher to be notified when the system
  307. // exits memory pressure.
  308. StartHighMemoryNotificationWatcher();
  309. }
  310. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  311. OnHighMemoryNotification() {
  312. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  313. base::UmaHistogramMediumTimes(
  314. "Discarding.WinOSPressureSignals.LowMemorySessionLength",
  315. base::TimeTicks::Now() - critical_pressure_session_begin_);
  316. critical_pressure_session_begin_ = base::TimeTicks();
  317. critical_pressure_notification_timer_.Stop();
  318. voter_->SetVote(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
  319. /* notify = */ false);
  320. // Start the low memory notification watcher to be notified the next time the
  321. // system hits memory pressure.
  322. StartLowMemoryNotificationWatcher();
  323. }
  324. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  325. StartLowMemoryNotificationWatcher() {
  326. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  327. DCHECK(base::SequencedTaskRunnerHandle::IsSet());
  328. memory_notification_watcher_ =
  329. std::make_unique<MemoryPressureWatcherDelegate>(
  330. base::win::ScopedHandle(::CreateMemoryResourceNotification(
  331. ::LowMemoryResourceNotification)),
  332. base::BindOnce(
  333. &SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  334. OnLowMemoryNotification,
  335. base::Unretained(this)));
  336. }
  337. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  338. StartHighMemoryNotificationWatcher() {
  339. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  340. memory_notification_watcher_ =
  341. std::make_unique<MemoryPressureWatcherDelegate>(
  342. base::win::ScopedHandle(::CreateMemoryResourceNotification(
  343. ::HighMemoryResourceNotification)),
  344. base::BindOnce(
  345. &SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  346. OnHighMemoryNotification,
  347. base::Unretained(this)));
  348. }
  349. void SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator::
  350. WaitForHighMemoryNotificationForTesting(base::OnceClosure closure) {
  351. // If the timer isn't running then it means that the high memory notification
  352. // has already been received.
  353. if (!critical_pressure_notification_timer_.IsRunning()) {
  354. std::move(closure).Run();
  355. return;
  356. }
  357. memory_notification_watcher_->SetCallbackForTesting(base::BindOnce(
  358. [](SystemMemoryPressureEvaluator::OSSignalsMemoryPressureEvaluator*
  359. evaluator,
  360. base::OnceClosure closure) {
  361. evaluator->OnHighMemoryNotification();
  362. std::move(closure).Run();
  363. },
  364. base::Unretained(this), std::move(closure)));
  365. }
  366. } // namespace win
  367. } // namespace memory_pressure