blob: 22291da09ba9191054f26eb3c8bce70470917389 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/chromeos/memory_pressure_observer_chromeos.h"
#include "base/process/process_metrics.h"
#include "base/time/time.h"
namespace base {
namespace {
// The time between memory pressure checks.
const int kMemoryPressureIntervalInMS = 1000;
// Converts free percent of memory into a memory pressure value.
MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
int memory_fill_level) {
if (memory_fill_level < 70)
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
return memory_fill_level < 90 ?
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE :
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
}
// Gets the used ChromeOS memory in percent.
int GetUsedMemoryInPercent() {
base::SystemMemoryInfoKB info;
if (!base::GetSystemMemoryInfo(&info)) {
VLOG(1) << "Cannot determine the free memory of the system.";
return 0;
}
// TODO(skuhne): Instead of adding the kernel memory pressure calculation
// logic here, we should have a kernel mechanism similar to the low memory
// notifier in ChromeOS which offers multiple pressure states.
// To track this, we have crbug.com/381196.
// The available memory consists of "real" and virtual (z)ram memory.
// Since swappable memory uses a non pre-deterministic compression and
// the compression creates its own "dynamic" in the system, it gets
// de-emphasized by the |kSwapWeight| factor.
const int kSwapWeight = 4;
// The total memory we have is the "real memory" plus the virtual (z)ram.
int total_memory = info.total + info.swap_total / kSwapWeight;
// The kernel internally uses 50MB.
const int kMinFileMemory = 50 * 1024;
// Most file memory can be easily reclaimed.
int file_memory = info.active_file + info.inactive_file;
// unless it is dirty or it's a minimal portion which is required.
file_memory -= info.dirty + kMinFileMemory;
// Available memory is the sum of free, swap and easy reclaimable memory.
int available_memory =
info.free + info.swap_free / kSwapWeight + file_memory;
DCHECK(available_memory < total_memory);
int percentage = ((total_memory - available_memory) * 100) / total_memory;
return percentage;
}
} // namespace
MemoryPressureObserverChromeOS::MemoryPressureObserverChromeOS()
: current_memory_pressure_level_(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
StartObserving();
}
MemoryPressureObserverChromeOS::~MemoryPressureObserverChromeOS() {
StopObserving();
}
void MemoryPressureObserverChromeOS::StartObserving() {
timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kMemoryPressureIntervalInMS),
base::Bind(&MemoryPressureObserverChromeOS::CheckMemoryPressure,
base::Unretained(this)));
}
void MemoryPressureObserverChromeOS::StopObserving() {
// If StartObserving failed, StopObserving will still get called.
timer_.Stop();
}
void MemoryPressureObserverChromeOS::CheckMemoryPressure() {
MemoryPressureListener::MemoryPressureLevel old_pressure =
current_memory_pressure_level_;
MemoryPressureListener::MemoryPressureLevel new_pressure =
GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent());
if (old_pressure != new_pressure) {
current_memory_pressure_level_ = new_pressure;
// Everything but NONE will be sent to the listener.
if (new_pressure != MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE)
MemoryPressureListener::NotifyMemoryPressure(new_pressure);
}
}
} // namespace base