Reading notes, if necessary, please indicate the author: Yuloran (t.cn/EGU6c76)

preface

As I mentioned in my previous article, Analyzing and Optimizing The Memory footprint of Android Apps, to prevent your device from stashing, crashing, or restarting if you have too few Cached Pages, Android introduced LowMemoryKiller (from Linux OOM Killer) to reclaim resources occupied by processes with lower priorities in advance to ensure a better user experience. The process priority list is maintained by the SystemServer process:

The Cached process list uses the LRU algorithm.

Each Java process has an associated ProcessRecord object, whose member variable, curAdj, represents the priority of the process in its current state:

    int curAdj; // Current OOM adjustment for this process
Copy the code

When the available memory of the device is lower than the LMK threshold, the LMK kills processes step by step and releases resources occupied by processes based on their priorities.

It is recommended to read my first four blog posts to get a comprehensive and in-depth understanding of Android memory:

  • Java Virtual Machine Memory Model and GC Algorithm in Detail
  • Android Virtual Machine Vs Java Virtual Machine
  • Analyzing and Optimizing The Memory Footprint of Android Apps
  • Android Memory Leaks

This post is the last in the Android memory series.

The lifecycle of a process

When LMK kills a process, it will follow the order of empty process -> background process -> server process -> visible process -> foreground process:

Foreground process

Processes that are necessary for the user’s current operation. A process is considered a foreground process if it meets any of the following criteria:

  • Hosting the Activity the user is interacting with (the Activity’s onResume() method has been called)
  • Hosts a Service that is bound to the Activity the user is interacting with
  • Host Service running “foreground” (Service called startForeground())
  • Hosting a Service (onCreate(), onStart(), or onDestroy()) that is performing a lifecycle callback
  • Host the BroadcastReceiver that is executing its onReceive() method

Typically, there are not many foreground processes at any given time. The system terminates them only when it is absolutely necessary that there is not enough memory to support them while still running.

Visible process

A process that does not have any foreground components but still affects what the user sees on the screen. A process is considered visible if it meets any of the following criteria:

  • Hosts an Activity (whose onPause() method has been called) that is not in the foreground but is still visible to the user. This might happen, for example, if the foreground Activity starts a dialog.
  • Hosts a Service bound to a visible (or foreground) Activity.

Visible processes are considered extremely important and will not be terminated unless necessary to keep all foreground processes running at the same time.

Service process

A process that is running a service started with the startService() method and does not belong to either of the higher categories of processes described above.

Although server processes are not directly related to what the user sees, they are usually performing some action that the user cares about (for example, playing music in the background or downloading data from the network). Therefore, unless there is not enough memory to keep all foreground and visible processes running at the same time, the system will leave the server processes running.

Background processes

The process that contains an Activity that is currently invisible to the user (the Activity’s onStop() method has been called).

These processes have no direct impact on the user experience, and the system may terminate them at any time to reclaim memory for foreground, visible, or server processes. There are usually many background processes running, so they are saved in the LRU (Least Recently used) list to ensure that the process containing the Activity the user recently viewed is the last to terminate.

An empty process

A process that does not contain any active application components.

The sole purpose of keeping such processes is to be used as a cache to reduce the startup time needed to run components in it the next time. To balance overall system resources between the process cache and the underlying kernel cache, systems often kill these processes.

LowMemoryKiller

Low Memory Killer for Android is a modified Memory management mechanism based on the standard Linux Kernel’s OOM Killer. When the system runs out of memory, kill unimportant processes to free up memory. There are three key parameters of LMK:

  • Oom_adj: indicates the priority of a process. The higher the value is, the lower the priority is and the easier it is to be killed.
  • Oom_adj threshold: indicates the memory threshold of oOM_adj used in the Framework layer. The Android Kernel periodically checks whether the current remaining memory is lower than the threshold. If the remaining memory is lower than the threshold, the process with the largest value in oOM_ADJ is killed until the remaining memory is higher than the threshold.
  • Oom_score_adj: used at the Kernel layer. Converted from oom_adj, it is an actual parameter used to kill processes.

oom_adj

Definition of value range:

-> ProcessList (AOSP, master branch)

    // These are the various interesting memory levels that we will give to
    // the OOM killer. Note that the OOM killer only supports 6 slots, so we
    // can't give it a different value for every possible kind of process.
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };
Copy the code

The above is only the adj. used by LMK to kill processes, but there are actually more adj. defined in this class:

Constants defined A constant value meaning
NATIVE_ADJ – 1000. Native processes (not managed by the system)
SYSTEM_ADJ – 900. System processes
PERSISTENT_PROC_ADJ – 800. Persistent system processes, such as Telephony
PERSISTENT_SERVICE_ADJ – 700. Associated with systems or persistent processes
FOREGROUND_APP_ADJ 0 Foreground process
VISIBLE_APP_ADJ 100 Visible process
PERCEPTIBLE_APP_ADJ 200 Aware of processes, such as background music playback
BACKUP_APP_ADJ 300 The backup process
HEAVY_WEIGHT_APP_ADJ 400 Background heavyweight process, set in the system/rootdir/init.rc file
SERVICE_ADJ 500 Service process
HOME_APP_ADJ 600 The Home process
PREVIOUS_APP_ADJ 700 The last App process
SERVICE_B_ADJ 800 Services in B List (older, less likely to be used)
CACHED_APP_MIN_ADJ 900 Minimum adj for an invisible process
CACHED_APP_MAX_ADJ 906 Maximum adj for invisible processes
UNKNOWN_ADJ 1001 It usually means that the process will be cached and cannot obtain a definite value

ProcessList (AOSP, marshmallow-Release branch)

Rule: The greater the value, the lower the importance, and the easier the process is to be killed.

When the LowMemoryKiller mechanism is triggered, you can analyze the state in which a process is killed based on the ADJ value of the process in the log.

oom_adj threshold

Definition of value range:

-> ProcessList (AOSP, master branch)

    // The actual OOM killer memory levels we are using.
    private final int[] mOomMinFree = new int[mOomAdj.length];
Copy the code

The specific value is obtained through conversion of the following two variables:

    // These are the low-end OOM level limits. This is appropriate for an
    // HVGA or smaller phone with less than 512MB. Values are in KB.
    private final int[] mOomMinFreeLow = new int[] {
            12288.18432.24576.36864.43008.49152
    };
    // These are the high-end OOM level limits. This is appropriate for a
    // 1280x800 or larger screen with around 1GB RAM. Values are in KB.
    private final int[] mOomMinFreeHigh = new int[] {
            73728.92160.110592.129024.147456.184320
    };
Copy the code

Array initialization or update methods:

    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
        int minSize = 480*800;  //  384000
        int maxSize = 1280*800; // 1024000 230400 870400.264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        floatscale = scaleMem > scaleDisp ? scaleMem : scaleDisp; . omitfinal boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
        for (int i=0; i<mOomAdj.length; i++) {
            int low = mOomMinFreeLow[i];
            int high = mOomMinFreeHigh[i];
            if (is64bit) {
                // Increase the high min-free levels for cached processes for 64-bit
                if (i == 4) high = (high*3) /2;
                else if (i == 5) high = (high*7) /4;
            }
            mOomMinFree[i] = (int)(low + ((high-low)*scale)); }... omitif (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i<mOomAdj.length; i++) {
                // Divided by PAGE_SIZE, so minfree units are pages, and 4KB
                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
                buf.putInt(mOomAdj[i]);
            }
            // Send LMK_TARGET to the LMKD process.
            / / the oom_adj threshold write "/ sys/module/lowmemorykiller/parameters/minfree"
            / / the oom_adj write "/ sys/module/lowmemorykiller/parameters/adj." "
            writeLmkd(buf);
            SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
        }
        / / GB: 2 2048307 2409 6614 4716 8819
        / / HC: 8192102 40122 88143 36163 84204 80
    }
Copy the code

This method is called during an ActivityManagerService call to updateConfiguration() to initialize or update the threshold size depending on the resolution of the device.

oom_score_adj

The value range is defined in the Linux Kernel:

-> oom.h

#ifndef _UAPI__INCLUDE_LINUX_OOM_H
#define _UAPI__INCLUDE_LINUX_OOM_H

/* * /proc/
      
       /oom_score_adj set to OOM_SCORE_ADJ_MIN disables oom killing for * pid. */
      
#define OOM_SCORE_ADJ_MIN	(-1000)
#define OOM_SCORE_ADJ_MAX	1000

/* * /proc/
      
       /oom_adj set to -17 protects from the oom killer for legacy * purposes. */
      
#define OOM_DISABLE (-17)
/* inclusive */
#define OOM_ADJUST_MIN (-16)
#define OOM_ADJUST_MAX 15

#endif /* _UAPI__INCLUDE_LINUX_OOM_H */
Copy the code

The conversion method from oOM_adj to oom_score_adj is defined in the Linux Driver:

-> lowmemorykiller.c

static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
{
	if (oom_adj == OOM_ADJUST_MAX)
		return OOM_SCORE_ADJ_MAX;
	else
		return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
}
Copy the code

The working process

This section describes the workflow of LMK

Start the LMKD

LMKD is a daemon started by the init process by parsing init.rc. LMKD creates a socket named LMKD. The node is located in /dev/socket/lmkd. The socket is used to interact with the upper framework:

service lmkd /system/bin/lmkd
    class core
    group root readproc
    critical
    socket lmkd seqpacket 0660 system system
    writepid /dev/cpuset/system-background/tasks
Copy the code

Once LMKD is started, it enters a circular wait state and accepts three commands from ProcessList:

The command function methods
LMK_TARGET Initialize the oom_adj ProcessList::setOomAdj()
LMK_PROCPRIO Update the oom_adj ProcessList::updateOomLevels()
LMK_PROCREMOVE Remove process (temporarily useless) ProcessList::remove()

Initialize the oom_adj

In ActivityManagerService call updateConfiguration () in the process of invokes the ProcessList: : updateOomLevels () method, adjust the size of the threshold according to the resolution of the equipment, With the LMK_TARGET command, Inform LMKD (low memory killer deamon) respectively to the/sys/module/lowmemorykiller/parameters minfree and adj node to write the corresponding information in the directory:

Update the oom_adj

The ProcessList::setOomAdj() method is called when applyOomAdjLocked() is called by ActivityManagerService, using LMK_PROCPRIO, Tell LMKD to write oomadj to /proc/process/oom_score_adj:

D:\Android\projects\wanandroid_java>adb shell ps | findstr wanandroid
u0_a71    6461  1285  1177696 84024 SyS_epoll_ b131e424 S com.yuloran.wanandroid_java

D: \Android\projects\wanandroid_java>adb shell ls /proc/ 6461 /... omitoom_adj
oom_score
oom_score_adj.Copy the code

ActivityManagerService calls applyOomAdjLocked() to update the process status and oOM_adj based on the life cycle of the four managed components of the current application process.

Process status table:

-> ActivityManager.java (AOSP, branch: master)

Constants defined A constant value meaning
PROCESS_STATE_UNKNOWN – 1 The process status is not real
PROCESS_STATE_PERSISTENT 0 Persistent system process
PROCESS_STATE_PERSISTENT_UI 1 Persistent system processes and are performing UI operations
PROCESS_STATE_TOP 2 Has the Top Activity visible to the current user
PROCESS_STATE_FOREGROUND_SERVICE 3 A process that hosts a foreground Service
PROCESS_STATE_BOUND_FOREGROUND_SERVICE 4 A process that hosts a system-bound foreground Service
PROCESS_STATE_IMPORTANT_FOREGROUND 5 Processes that are important to the user are perceived by the user
PROCESS_STATE_IMPORTANT_BACKGROUND 6 Users cannot perceive the existence of important processes
PROCESS_STATE_TRANSIENT_BACKGROUND 7 Process is in the background transient so we will try to keep running.
PROCESS_STATE_BACKUP 8 Background process running backup/restore operation
PROCESS_STATE_SERVICE 9 Background process, and service is running
PROCESS_STATE_RECEIVER 10 Background process and running receiver
PROCESS_STATE_TOP_SLEEPING 11 Same as PROCESS_STATE_TOP, but the device is sleeping at this point
PROCESS_STATE_HEAVY_WEIGHT 12 Background process, but cannot execute restore, so try to avoid killing the process
PROCESS_STATE_HOME 13 Background process with home Activity
PROCESS_STATE_LAST_ACTIVITY 14 A background process with the Activity that was last displayed
PROCESS_STATE_CACHED_ACTIVITY 15 The process is cached and contains Activity
PROCESS_STATE_CACHED_ACTIVITY_CLIENT 16 The process is in cached state and is the client process of another cached process (containing the Activity)
PROCESS_STATE_CACHED_RECENT 17 The process is cached and contains an Activity corresponding to the current most recent task
PROCESS_STATE_CACHED_EMPTY 18 The process is cached and empty
PROCESS_STATE_NONEXISTENT 19 Process that does not exist

The OOM_adj of a process in different states is different.

Kill and remove the process

When ActivityManagerService calls updateOomAdjLocked(), it determines whether the process needs to be killed. If so, the ProceeRecord::kill() method is called to kill the process:

Note: at present LMK_PROCREMOVE command is temporarily useless, that is, no meaningful code has been executed.

conclusion

Android memory series so far, all over 😁. If there are mistakes, but also hope to give advice 🤝.

The resources

  • Android Process Lifecycle and ADj.
  • Ad_algorithm for Android Process Scheduling
  • Android LowMemoryKiller Principle Analysis
  • AOSP