Memory Limiter

Android 17 and higher supports the memory limiter, which is a system service that monitors and limits the memory usage of application processes using Linux cgroup v2. The Memory Limiter prevents individual apps from consuming excessive system memory, which reduces system-wide memory pressure and prevents aggressive out-of-memory (OOM) killing of critical processes.

Mechanism

The Memory Limiter integrates with the Activity Manager Service (AMS) to track process lifecycle events and state changes. The Memory Limiter enforces memory limits using the Linux kernel cgroup v2 file system.

To use the Memory Limiter, the device kernel must support cgroup v2 and the memory controller. The service specifically relies on the following attributes:

memory.high
A soft limit. When exceeded, the process is throttled and the kernel attempts to reclaim memory from it.
memory.swap.max
Limits the amount of swap space the process can use.

Impact on apps

Apps that don't exceed their memory limits aren't affected by the Memory Limiter.

When an app crosses its memory.high limit, the kernel evicts the app's file-backed memory and swaps out its anonymous memory to keep the app within the limit. As a result of eviction and swap, the app might run slower.

At the extreme, if the app continues allocating anonymous memory and the device runs out of swap space, the app might fail to allocate memory, and as a result is likely to crash.

Process monitoring

The Memory Limiter monitors app processes (UID >= 10000) by default. System processes are generally exempt to help verify core system stability.

The Memory Limiter assigns memory limits based on the state of the process:

  • Visible processes are perceptible to the user, such as foreground activities, foreground services, or other jank-perceptible states.

  • Not visible processes are background processes that aren't interacting with or visible to the user.

The following table maps specific process states to memory limits:

Process stateMemory limit
PERSISTENTUnrestricted
PERSISTENT_UIUnrestricted
TOPVisible
BOUND_TOPVisible
FOREGROUND_SERVICENot visible
BOUND_FOREGROUND_SERVICENot visible
IMPORTANT_FOREGROUNDVisible
IMPORTANT_BACKGROUNDNot visible
TRANSIENT_BACKGROUNDNot visible
BACKUPNot visible
SERVICENot visible
RECEIVERNot visible
TOP_SLEEPINGVisible
HEAVY_WEIGHTNot visible
HOMENot visible
LAST_ACTIVITYNot visible
CACHED_ACTIVITYCached
CACHED_ACTIVITY_CLIENTCached
CACHED_RECENTCached
CACHED_EMPTYCached

In the cached state, processes are frozen and then maximally reclaimed.

When a process exceeds its assigned memory.high limit, the Memory Limiter detects the event and can trigger debugging actions, such as capturing a memory profile or logging an anomaly to statsd.

Configuration

Configure the Memory Limiter using an XML file located on the vendor partition. Configuration lets you tune absolute memory limits based on the specific memory constraints of the device.

  • File path: /vendor/etc/memory-limiter-config.xml

  • Default configuration: If the configuration file isn't found, or if it's unreadable or invalid, the Memory Limiter is disabled.

XML format

The configuration file follows the schema defined in memory-limiter-config.xsd. The file lets you define multiple limit sets; the service chooses the best match based on the device's available RAM. All memory values are defined in units of mebibytes (MiB).

<MemoryLimiterConfig>
  <version>1</version>
  <configList>
    <limitSet>
      <!-- Limits for a phone with at least 14G of ram: 8G/4G/4G/4G -->
      <minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
      <memVisible>8192</memVisible>
      <memNotVisible>4096</memNotVisible>
      <swapVisible>4096</swapVisible>
      <swapNotVisible>4096</swapNotVisible>
    </limitSet>
  </configList>
</MemoryLimiterConfig>
version
A positive integer identifying the configuration version. This must be 1.
minimumRequiredMemTotal
The minimum required available system memory for this limit set to be valid.
memVisible
The memory limit (memory.high) allowed for visible processes.
memNotVisible
The memory limit (memory.high) allowed for not visible processes.
swapVisible
The swap limit (memory.swap.max) allowed for visible processes.
swapNotVisible
The swap limit (memory.swap.max) allowed for not visible processes.

Modify configuration

To change system-wide limits, follow these steps:

  1. Modify /vendor/etc/memory-limiter-config.xml.
  2. Reboot the device or restart system_server for the changes to take effect.

Shell commands

The am memory-limiter command lets you and developers interact with the service at run time for development and testing:

am memory-limiter <SUB-COMMAND>

status

The status sub-command reports the operational status of the Memory Limiter:

adb shell am memory-limiter status

Example output:

Memory limiter
  enabled                  monitoring=true          ignored=none
  visibleMem=1948MB        visibleSwap=974MB
  notVisibleMem=974MB      notVisibleSwap=487MB
  started=36               watched=36               watch-failed=0
  events=0                 processes=36             process-hwm=36

Key fields in the output include:

monitoring
Indicates whether the limiter is actively watching processes.
visibleMem and notVisibleMem
Indicate the calculated absolute memory limits for each state.
events
The number of times a process has exceeded its limit.
processes
The number of monitored processes.

ignore

The ignore sub-command temporarily excludes a UID or all processes from being limited. This action is useful for performance testing or when allowing a specific app to exceed its limits.

adb shell am memory-limiter ignore 10087  // Ignore a specific UID
adb shell am memory-limiter ignore all    // Ignore all processes (effectively disables limiting)
adb shell am memory-limiter ignore none   // Resume normal operation

manual

The manual sub-command overrides the calculated limits for a specific process (by process ID, or PID) with a custom absolute value in megabytes (MB):

adb shell am memory-limiter manual 1234 1024   // Set a 1024 MB limit for PID 1234
adb shell am memory-limiter manual 1234 none // Remove the manual override for PID 1234

Manual overrides apply only to the lifecycle of the process. If the process restarts, it returns to the default limits based on its state.