The Android low memory killer daemon (lmkd
) process monitors the memory state of a running Android system and reacts to high memory pressure by killing the least essential processes to keep the system performing at acceptable levels.
About memory pressure
An Android system running multiple processes in parallel may encounter situations when system memory is exhausted and processes that require more memory experience noticeable delays. Memory pressure, a state in which the system is running short on memory, requires Android to free memory (to alleviate the pressure) by throttling or killing unimportant processes, requesting processes to free noncritical cached resources, and so on.
Historically, Android monitored system memory pressure using an in-kernel low memory killer (LMK) driver, a rigid mechanism that depends on hard-coded values. As of kernel 4.12, the LMK driver is removed from the upstream kernel and the userspace lmkd
performs memory monitoring and process killing tasks.
Pressure stall information
Android 10 and higher support a new lmkd
mode that uses kernel pressure stall information (PSI) monitors for memory pressure detection. The PSI patchset in the upstream kernel (backported to 4.9 and 4.14 kernels) measures the amount of time that tasks are delayed as a result of memory shortages. As these delays directly affect user experience, they represent a convenient metric for determining memory pressure severity. The upstream kernel also includes PSI monitors that allow privileged userspace processes (such as lmkd
) to specify thresholds for these delays and to subscribe to events from the kernel when a threshold is breached.
PSI monitors versus vmpressure signals
Because the vmpressure
signals (generated by the kernel for memory pressure detection and used by lmkd
) often include numerous false positives, lmkd
must perform filtering to determine if the memory is under real pressure. This results in unnecessary lmkd
wakeups and the use of additional computational resources. Using PSI monitors results in more accurate memory pressure detection and minimizes filtering overhead.
Use PSI monitors
To use PSI monitors instead of vmpressure
events, configure the ro.lmk.use_psi
property. The default is true
, making PSI monitors the default mechanism of memory pressure detection for lmkd
. Because PSI monitors require kernel support, the kernel must include the PSI backport patches and be compiled with PSI support enabled (CONFIG_PSI=y
).
Drawbacks of in-kernel LMK driver
Android deprecates the LMK driver due to a number of issues, including:
- Low-RAM devices had to be tuned aggressively, and even then would perform poorly on workloads with large file-backed active pagecache. The poor performance resulted in thrashing and no kills.
- The LMK kernel driver relied on free-memory limits, with no scaling based on the memory pressure.
- Because of the rigidity of the design, partners often customized the driver so that it would work on their devices.
- The LMK driver hooked into the slab shrinker API, which wasn't designed for heavy operations such as searching for targets and killing them, which slowed down the
vmscan
process.
Userspace lmkd
The userspace lmkd
implements the same functionality as the in-kernel driver but uses existing kernel mechanisms to detect and estimate memory pressure. Such mechanisms include using kernel-generated vmpressure
events or pressure stall information (PSI) monitors to get notifications about memory pressure levels, and using memory cgroup features to limit the memory resources allocated to each process based on process importance.
Use userspace lmkd in Android 10
In Android 9 and higher, userspace lmkd
activates if an in-kernel LMK driver isn't detected. Because userspace lmkd
requires kernel support for memory cgroups, the kernel must be compiled with the following configuration settings:
CONFIG_ANDROID_LOW_MEMORY_KILLER=n CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y
Kill strategies
Userspace lmkd
supports kill strategies based on vmpressure
events or PSI monitors, their severity, and other hints such as swap utilization. Kill strategies differ between low-memory and high-performance devices:
- On low-memory devices, the system should tolerate higher memory pressure as a normal mode of operation.
- On high-performance devices, memory pressure should be viewed as an abnormal situation and fixed before it affects overall performance.
You can configure the kill strategy using the ro.config.low_ram
property.
Userspace lmkd
also supports a legacy mode in which it makes kill decisions using the same strategies as the in-kernel LMK driver (that is, free memory and file cache thresholds). To enable legacy mode, set the ro.lmk.use_minfree_levels
property to true
.
Configure lmkd
Configure lmkd
for a specific device using the following properties.
Property | Use | Default |
---|---|---|
ro.config.low_ram | Specify if the device is a low-RAM or high-performance device. | false |
ro.lmk.use_psi | Use PSI monitors (instead of vmpressure events). | true |
ro.lmk.use_minfree_levels | Use free memory and file cache thresholds for making process kill decisions (that is, match the functionality of the in-kernel LMK driver). | false |
ro.lmk.low | The minimum oom_adj score for processes eligible to be killed at low vmpressure level. | 1001 (disabled) |
ro.lmk.medium | The minimum oom_adj score for processes eligible to be killed at medium vmpressure level. | 800 (cached or nonessential services) |
ro.lmk.critical | The minimum oom_adj score for processes eligible to be killed at critical vmpressure level. | 0 (any process) |
ro.lmk.critical_upgrade | Enable upgrade to critical level. | false |
ro.lmk.upgrade_pressure | The maximum mem_pressure at which the level is upgraded because the system is swapping too much. | 100 (disabled) |
ro.lmk.downgrade_pressure | The minimum mem_pressure at which a vmpressure event is ignored because enough free memory is still available. | 100 (disabled) |
ro.lmk.kill_heaviest_task | Kill the heaviest eligible task (best decision) versus any eligible task (fast decision). | false |
ro.lmk.kill_timeout_ms | Duration in milliseconds after a kill when no additional kill will be done. | 0 (disabled) |
ro.lmk.debug | Enable lmkd debug logs. | false |
Example device configuration:
PRODUCT_PROPERTY_OVERRIDES += \ ro.lmk.low=1001 \ ro.lmk.medium=800 \ ro.lmk.critical=0 \ ro.lmk.critical_upgrade=false \ ro.lmk.upgrade_pressure=100 \ ro.lmk.downgrade_pressure=100 \ ro.lmk.kill_heaviest_task=true
Userspace lmkd in Android 11
Android 11 improves the lmkd
by introducing a new killing strategy. The killing strategy uses a PSI mechanism for memory pressure detection introduced in Android 10. lmkd
in Android 11 accounts for memory resource use levels and thrashing to prevent memory starvation and performance degradation. This killing strategy replaces previous strategies and can be used on both high-performance and low-RAM (Android Go) devices.
Kernel requirements
For Android 11 devices, lmkd
requires the following kernel features:
- Include PSI patches and enable PSI (backports available in Android common kernels 4.9, 4.14, and 4.19).
- Include PIDFD support patches (backports available in Android common kernels 4.9, 4.14, and 4.19).
- For low-RAM devices, include memory cgroups.
The kernel must be compiled with the following configuration settings:
CONFIG_PSI=y
Configure lmkd in Android 11
The memory killing strategy in Android 11 supports the tuning knobs and defaults listed below. These features work on both high-performance and low-RAM devices.
Property | Use | Default | |
---|---|---|---|
High performance | Low RAM | ||
ro.lmk.psi_partial_stall_ms | The partial PSI stall threshold, in milliseconds, for triggering low memory notification. If the device receives memory pressure notifications too late, decrease this value to trigger earlier notifications. If memory pressure notifications trigger unnecessarily, increase this value to make the device less sensitive to noise. | 70 | 200 |
ro.lmk.psi_complete_stall_ms | The complete PSI stall threshold, in milliseconds, for triggering critical memory notifications. If the device receives critical memory pressure notifications too late, decrease this value to trigger earlier notifications. If critical memory pressure notifications trigger unnecessarily, increase this value to make the device less sensitive to noise. | 700 | |
ro.lmk.thrashing_limit | The max amount of workingset refaults as a percentage of the total file-backed pagecache size. Workingset refaults above this value mean that the system is considered to be thrashing its pagecache. If the performance of the device is affected during memory pressure, decrease the value to limit thrashing. If the performance of the device is killed unnecessarily for thrashing reasons, increase the value to allow more thrashing. | 100 | 30 |
ro.lmk.thrashing_limit_decay | The thrashing threshold decay expressed as a percentage of the original threshold used to lower the threshold when the system doesn’t recover, even after a kill. If continuous thrashing produces unnecessary kills, decrease the value. If the response to continuous thrashing after a kill is too slow, increase the value. | 10 | 50 |
ro.lmk.swap_util_max | The max amount of swapped memory as a percentage of the total swappable memory. When swapped memory grows over this limit, it means that the system swapped most of its swappable memory and is still under pressure. This can happen when non-swappable allocations are generating memory pressure which can't be relieved by swapping because most of the swappable memory is already swapped out. The default value is 100, which effectively disables this check. If the performance of the device is affected during memory pressure while swap utilization is high and the free swap level isn't dropping to ro.lmk.swap_free_low_percentage , decrease the value to limit swap utilization. | 100 | 100 |
The following old tuning knobs also work with the new killing strategy.
Property | Use | Default | |
---|---|---|---|
High performance | Low RAM | ||
ro.lmk.swap_free_low_percentage | The level of free swap as a percentage of the total swap space. `lmkd` uses this value as a threshold for when to consider the system as swap space starved. If `lmkd` kills while there's too much space in swap, decrease the percentage. If `lmkd` kills happen too late, allowing OOM kills to happen, increase the percentage. | 20 | 10 |
ro.lmk.debug | This enables `lmkd` debug logs. Enable debug while tuning. | false |