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