A brief analysis of Android oom mechanism

This article is mainly based on Android11, and organizes Android’s knowledge about oom killer, lowmemorykiller (lmk lmkd), and kswapd. The source code has not been analyzed in depth.

Table of contents

linux oom

The relationship between oom_adj, oom_score and oom_score_adj

android low memory killer(lmk)

android app oom

kswapd

zRAM

kswapd and lmkd

Summarize


linux oom

  • Killing process rules: According to priority, /proc/pid/oom_score (calculated by oom_score_adj) The larger the value, the easier it is to be selected by the oom killer.
  • The linux oom mechanism is built in the kernel
  • In order to prevent your own process from being easily killed, users can lower the value of oom_score_adj. If you directly echo -17 > /proc/$PID/oom_adj, you can let the oom killer ignore the process.
  • The specific implementation mechanism is here oom_kill.c .
  • Linux oom is for the entire system. Oom is triggered when allocating memory (alloc_page(), which will definitely be used when allocating memory, and memory is managed by pages). If insufficient memory is found, oom killer will be triggered, and the most effective method to kill is calculated. For those processes, the memory will be recovered after killing them (I don’t know whether to kill one or several).

The relationship between oom_adj, oom_score and oom_score_adj

  • oom_score: The value calculated by the kernel based on the oom_score_adj value and the amount of memory used by the process. Later, both oom killer and lmkd will decide which processes to kill based on this value.
  • oom_score_adj: The value is -1000---1000. If the value is -1000, it will turn off the management of oom killer (such as init, surfaceflinger and other native processes). It and oom_adj are both interfaces left by the kernel for the user space to change the priority of the process to be killed. AMS adjusts the app adj value by encapsulating the pid, uid, and adj into a LMK_PROCPRIO message package and passing it to lmkd through the unix socket. This is changed through lmkd. value.
//frameworks/base/services/core/java/com/android/server/am/processlist.java
 public static void setOomAdj(int pid, int uid, int amt) {
        //...
        ByteBuffer buf = ByteBuffer.allocate(4 * 4);
        buf.putInt(LMK_PROCPRIO);
        buf.putInt(pid);
        buf.putInt(uid);
        buf.putInt(amt);
        writeLmkd(buf, null);//通过unix socket写给lmkd
        //...
    }
//system/memory/lmkd/lmkd.cpp
static void ctrl_command_handler(int dsock_idx) {
    //...
        case LMK_PROCPRIO:
        cmd_procprio(packet, nargs, &cred);
        break;
    //...
}

static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {
    //...
    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
    snprintf(val, sizeof(val), "%d", params.oomadj);//ams传过来的adj值
    if (!writefilestring(path, val, false)) {
        return;
    }

    //...
}
  • oom_adj: It is the old interface before oom_score_adj. It is retained for compatibility. The value is -16--15. If the value is -17, the management of oom killer will be turned off. Now if the user changes this value, it will be converted to oom_score_adj later in the kernel.
//kernel/msm-4.19/Documentation/filesystems/proc.txt

For backwards compatibility with previous kernels, /proc/<pid>/oom_adj may also
be used to tune the badness score.  Its acceptable values range from -16
(OOM_ADJUST_MIN) to +15 (OOM_ADJUST_MAX) and a special value of -17
(OOM_DISABLE) to disable oom killing entirely for that task.  Its value is
scaled linearly with /proc/<pid>/oom_score_adj.

android low memory killer(lmk)

  • Android mainly uses lmkd in user space to manage, mainly for app-oriented memory management processes (generally system process oom_adj is less than 0, lmkd only kills processes with oom_adj >= 0), and also uses oom_adj and oom_score_adj to calculate the oom_score score. To decide which processes to kill, we just add a process classification and timing detection mechanism. Each level has its own threshold value. Once triggered, a batch of processes will be killed according to the level.
  • When to use oom killer? Never used it.
  • Compared with linux oom killer, lmkd is effective in android. The code of linux oom android is compiled into the kernel, but it will not trigger. It is always lmkd that triggers first (I don’t know why, is it because the threshold of lmkd is lower than that of oom killer?). lmkd is based on the threshold of the entire system. If it is lower than a certain threshold, lmk is triggered. It has its own independent process lmkd, while Linux oom is triggered when the memory allocation is insufficient.
  • oom memory guard table

  • This table defines a corresponding relationship. Each warning value corresponds to an importance value. When the available memory of the system is lower than a certain warning value, all programs that are greater than the importance value corresponding to the warning value will be killed. For example, when the available memory is less than 6144 * 4K = 24MB, start killing all processes with adj 15; when the available memory is less than 5632 * 4K = 22MB, start killing all processes with adj 14.
  • Android AMS will change the oom_score_adj value of the app according to the status of the app and the LRU mechanism, thereby affecting the app's level in lmkd and the priority of being killed.

android app oom

To maintain a multitasking environment, Android sets a hard cap on the heap size of each application. The exact maximum heap size for different devices depends on the overall available RAM size of the device. You may receive this if your app attempts to allocate more memory after reaching the heap capacity limit OutOfMemoryError.

Quoted from: https://developer.android.com/topic/performance/memory-overview?hl=zh-cn

kswapd

Compared with Linux, traditional kswapd uses disks as swap memory and swaps memory contents to disks;

android kswapd, uses memory (zRAM) as swap memory, and adds compression and decompression (actually it is also derived from Linux, but it is only used in Android. I think some Linux is useful but some are not, and there are zSwap, zCacha and so on. , those of Linux have not yet learned about it in detail).

When memory is insufficient, kswapd is triggered first, deleting the contents of clean pages to release memory, compressing the contents of dirty pages and writing them to zRAM. If the process associated with the dirty pages exits, zRAM will be released. If the memory is not enough after this, , trigger the low memory killer,

Picture from: https://developer.android.com/topic/performance/memory-management?hl=zh-cn#kswapd

zRAM

zRAM hardware also uses memory sticks to implement functions similar to swap. It is managed by kswapd. Data will be compressed and decompressed when entering and exiting zRAM. The zRAM space can be dynamically expanded and contracted according to the amount of data, or through /sys/block /zram0/disksize to view and configure. The essence is to exchange time for space, and exchange the decompression and compression of the CPU for more memory space (the specific compression algorithms are not yet understood).

zRAM is in the form of a block device. The driver code is in kernel/msm-4.19/drivers/block/zram/zram_drv.c. It creates a block device and provides an IO interface. The driver divides a piece of physical hardware memory for use when booting. User space interacts with zRAM through /sys/block/zram0,

kswapd and lmkd

When the memory is not enough, first trigger kswapd to perform memory compression. For example, Qualcomm Snapdragon 888 uses 4GB for swap. After 4GB is used to a certain extent (this level is not fixed, I haven’t figured out why yet, I tried it with an 8GB Qualcomm Snapdragon 888 mobile phone). times (sometimes 4GB is used up, sometimes 2GB is used, sometimes 1GB is used), lmkd starts to kill the process.

Summarize

Generally speaking, for Android, there are two services to manage insufficient memory, kswapd and lmkd. When the memory is insufficient, kswapd is first triggered to delete the memory contents of clean pages or compress the memory contents of dirty pages into ZRam and then release the memory. If it is not enough, trigger lmkd and decide which app processes to kill based on oom_score. AMS will change the app's oom_score_adj through lmkd based on the app status and the lru mechanism, thereby affecting the priority of the app being killed by lmkd.

reference:

https://blog.csdn.net/jun5753/article/details/105670683

https://jekton.github.io/2019/03/21/android9-lmk-lmkd/

https://blog.csdn.net/johnWcheung/article/details/87600413

https://developer.android.com/topic/performance/memory-management?hl=zh-cn#kswapd

https://kernel.meizu.com/zram-introduction.html

http://www.wowotech.net/memory_management/zram.html

https://developer.android.com/topic/performance/memory-overview?hl=zh-cn

kernel/msm-4.19/Documentation/filesystems/proc.txt

Guess you like

Origin blog.csdn.net/goodnight1994/article/details/118556198