blob: d991e3f16b10fa0709bf7141aaed7ac61e6d6c38 [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.
MemoryPressureObserverChromeOS::MemoryPressureLevel
GetMemoryPressureLevelFromFillLevel(
int memory_fill_level) {
if (memory_fill_level < 50)
return MemoryPressureObserverChromeOS::MEMORY_PRESSURE_LEVEL_LOW;
if (memory_fill_level < 75)
return MemoryPressureObserverChromeOS::MEMORY_PRESSURE_LEVEL_MODERATE;
if (memory_fill_level < 90)
return MemoryPressureObserverChromeOS::MEMORY_PRESSURE_LEVEL_HIGH;
return MemoryPressureObserverChromeOS::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_(MEMORY_PRESSURE_LEVEL_LOW) {
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() {
MemoryPressureLevel old_pressure = current_memory_pressure_level_;
MemoryPressureLevel new_pressure =
GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent());
if (old_pressure != new_pressure) {
current_memory_pressure_level_ = new_pressure;
switch (new_pressure) {
case MEMORY_PRESSURE_LEVEL_LOW:
// The |MemoryPressureListener| does currently not support this.
break;
case MEMORY_PRESSURE_LEVEL_MODERATE:
MemoryPressureListener::NotifyMemoryPressure(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
break;
case MEMORY_PRESSURE_LEVEL_HIGH:
// The |MemoryPressureListener| does currently not support this.
break;
case MEMORY_PRESSURE_LEVEL_CRITICAL:
MemoryPressureListener::NotifyMemoryPressure(
MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
break;
}
}
}
} // namespace base