LMKD in Userspace

This document describes the userspace lowmemorykiller daemon (lmkd) features added in Android 9 and how to configure them.

Historically, Android used in-kernel lowmemorykiller driver to handle memory pressure situations by killing non-essential processes. This mechanism is rigid and depends on hard-coded values. In addition, starting with kernel 4.12, the lowmemorykiller driver is excluded from the upstream kernel.

The userspace lmkd process implements the same functionality but with already existing kernel mechanisms to detect and estimate memory pressure. It uses vmpressure events generated by the kernel to get notifications about memory pressure levels. It also can use memory cgroup features to limit memory resources allocated to each process based on its importance.

How to switch to userspace lmkd

Starting in Android 9, userspace lmkd activates if an in-kernel lowmemorykiller driver is not detected. Note that userspace lmkd requires kernel support for memory cgroups. Therefore, to switch to userspace lmkd the kernel should be compiled with the following configuration settings:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

lmkd kill strategies

lmkd supports new kill strategies based on vmpressure events, their severity, and other hints like swap utilization, as well as legacy mode in which lmkd makes kill decisions just as the kernel lowmemorykiller driver did.

New kill strategies differ for low-memory vs high-performance devices. In cases of 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 that should be fixed before it affects overall performance. The ro.config.low_ram property allows for choosing one mode over the other. See Low RAM Configuration for instructions on setting this property.

In legacy mode, lmkd kill decisions are made based on free memory and file cache thresholds. This mode is enabled by setting the ro.lmk.use_minfree_levels property to true.

Configuring lmkd for specific device

Configure lmkd with the following properties:

Property Use Default Value
ro.config.low_ram Choose between low-memory vs. high-performance device. false
ro.lmk.use_minfree_levels Use free memory and file cache thresholds for making decisions when to kill. This mode works the same way kernel lowmemorykiller driver used to work. 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 non-essential 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 Enables upgrade to critical level. false
ro.lmk.upgrade_pressure The maximum mem_pressure at which level will be upgraded because system is swapping too much. 100
(disabled)
ro.lmk.downgrade_pressure The minimum mem_pressure* at which vmpressure event will be ignored because enough free memory is still available. 100
(disabled)
ro.lmk.kill_heaviest_task Kill heaviest eligible task (best decision) vs. any eligible task (fast decision). true
ro.lmk.kill_timeout_ms Duration in ms after a kill when no additional kill will be done. 0
(disabled)
ro.lmk.debug Enable lmkd debug logs. false

*Note: *mem_pressure = RAM usage / RAM_and_swap usage in %

Here is a device configuration example:

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