Android operating system memory recovery strategy

  • 2020-12-22 17:46:38
  • OfStack

Android is a mobile terminal operating system based on Linux kernel. In order to adapt to its special needs as a mobile platform operating system, Google has made a special design and optimization for it, so that the application is closed but does not exit, and the process recovery management is carried out by the operating system. In this paper, the process resource recovery mechanism of Android operating system is analyzed at two levels of Application Framework and Linux kernel, taking process as granularity. This article provides readers with a step forward understanding of the life cycle of Android applications to build applications more rationally and efficiently.

There are two levels of memory recovery in the Android operating system:

1. Default memory recovery, that is, the default recovery of Application Framework layer.

2. Kernel-level memory recovery.

Memory recovery in the Linux kernel: lowmemorykiller, OOM_killer.

Default memory collection :(the code is available in the ActivityManagerService.java class) the collection action entry activityIdleInternal().

The trigger point of memory recovery in Android system can be roughly divided into three situations.

First, the user program calls StartActivity() so that the current active Activity is overridden;

Second, the user presses back to exit the current application. Third, start a new application.

The final function interface to which these events trigger memory recovery is called is activityIdleInternal(). activityIdleInternal() is called when ActivityManagerService receives an asynchronous message IDLE_TIMEOUT_MSG or IDLE_NOW_MSG. IDLE_NOW_MSG is triggered by the switch of Activity and the change of Activiy focus. IDLE_TIMEOUT_MSG is triggered by the startup timeout of Activity, which is generally set to 10s. If one Activity still fails to start within 10s, an asynchronous message IDLE_TIMEOUT_MSG will be sent for resource recovery. The main task of activityIdleInternal() is to change the state information of Activity in the system and add it to a different state list. Its main functions are as follows:

First, call the scheduleAppGcsLocked() method to notify all ongoing tasks to garbage collect. scheduleAppGcsLocked() will schedule garbage collect of JVM to reclaim 1 portion of memory space, here simply notifies each process to check its own process garbage and schedule the recycling time, rather than synchronously.

Then, take all the contents of the mStoppingActivities and mFinishigActivities lists and store them in temporary variables. These two lists store activity objects with current states of stop and finishi, respectively. For the stop list, if the activity finish status is true, decide whether to stop immediately, if to stop immediately, call destroyActivityLocked() to notify the target process to call the onDestroy() method, otherwise, call resumeTopActivity() first to run the next Activity. If the finish status is false, the call to stopActivityLocked() notifies the client process to stop the Activity, which typically happens after the call to startActivity(). For the finish list, call destroyActivityLocked() directly to notify the client process to destroy the target Activity. Functions like destroyActivityLocked here don't really change memory usage, they just change their state to "allow recycling," which is in the trimApplications() function that will be called below.


private final void trimApplications() {
synchronized (this) {
// First remove any unused application processes whose package
// has been removed.
for (i=mRemovedProcesses.size()-1; i>=0; i--) {
(1)//kill process;
}
if (!updateOomAdjLocked()) {
(2)//do something default
}
// Finally, if there are too many activities now running, try to
// finish as many as we can to get back down to the limit.
(3)do something
}
}

(1) After the program has been executed to trimApplications(), the process in the mRemovedProcesses list is first checked. The mRemovedProcesses list includes processes from crash, processes that do not respond within 5 seconds and are selected by the user to be forcibly shut down, and processes that the application developer wants to kill by calling killBackgroundProcess. Call Process.killProcess to kill all such processes.

(2) Call the updateOomAdjLocked() function, if it returns successfully, indicating that the Linux kernel supports the setOomAdj() interface, updateOomAdjLocked will modify the value of adj and notify the linux kernel, which dynamically manages process resources (lowmemorykiller and oom_killer) based on the adj value and memory usage. If updateOomAdjLocked() returns false, the current system does not support the setOomAdj() interface, and the default resource collection is performed locally.

(3) Finally, if too much Activity is still running, recycle the excess Activity. Most of the code for trimApplications() deals with default recycling in the absence of Oom_killer, which is analyzed step 1 below (the location of the tag (2) in the code). The recycling process can be roughly described as follows.

Step 1: Get all currently running processes mLruProcesses, collating in mLruProcesses by most recent use time. Count the processes in mLruProcesses that cannot be shut down, including those running service, broadcast receiver, and so on.

Step 2. Set the current maximum number of processes running curMaxProcs = curMaxProcs + numServiceProcs (that is, the default maximum number of processes plus the number of processes running Service). If the current number of processes mRemovedProcesses. size() is greater than this, then traverse all currently running processes, killing those that meet the conditions and freeing up memory. The process is killed on the condition that it must be a non-ES148en process, that is, a non-system process, or an empty process, that no activity exists in the process. If a process with Activity is killed, it may shut down the program that the user is using, or make the recovery delay of the application larger, thus affecting the user experience. There must be no broadcast receiver. broadcast receiver 1 is generally waiting for an event to occur, and users do not want such programs to be forced to shut down by the system; The number of service in the process must be 0. It is likely that a process with service is providing some kind of service to one or more programs, such as the GPS location service. Killing such processes will prevent other processes from serving properly.

Step 3, review the currently running process again and relax the conditions for recycling again if ES160en.size () is still larger than curMaxProcs.

Step 4. The above three processes are all resource recycling for the entire process. After the above process is completed, the Activity resource is reclaimed at a smaller granularity. Similar to the above, list mLRUActivities stores all Activity currently running, with the same collation rule for least access. mLRUActivities.size () returns the number of Activity running in the system when it is greater than MAX_ACTIVITIES (MAX_ACTIVITIES is a constant with a general value of 20, which represents the maximum number of Activity that are allowed to coexist in the system). A portion of Activity that meets the criteria is reclaimed to reduce memory usage. Only the Activity memory resource is reclaimed here, and it does not kill the process, nor does it affect how the process runs. When a process needs to call the killed Activity, it can recover from the saved state, perhaps with a relatively long delay of one point.

Memory recovery in the Linux kernel

lowmemorykiller

The trimApplications() function performs a function called updateOomAdjLocked(), default memory collection if false is returned and default memory collection is not performed if true is returned.
updateOomAdjLocked will update a variable named adj for each process and inform the Linux kernel, which maintains a data structure containing adj (that is, a process table), checks system memory usage through lowmemorykiller, kills some processes and frees memory in case of insufficient memory.

Since all applications in the Android operating system run in a separate Dalvik virtual machine environment, the Linux kernel cannot know the running state of each process and therefore cannot maintain an appropriate adj value for each process, so a mechanism must be provided in Android Application Framework to dynamically update adj for each process. This is updateOomAdjLocked().

Android specifies five default recovery priorities based on the components running in the process and their state:

IMPORTANCE_FOREGROUND:
IMPORTANCE_VISIBLE:
IMPORTANCE_SERVICE:
IMPORTANCE_BACKGROUND:
IMPORTANCE_EMPTY:


Related articles: