Memory reclaim mechanism of Android operating system

Source: Internet
Author: User

Memory reclaim mechanism of Android operating system

 

Running Environment of Android APP

Android is an operating system for mobile terminals based on the Linux kernel. To adapt to its special needs as a mobile platform operating system, Google made a special design and optimization for it, making its process scheduling and resource management significantly different from that of Linux on other platforms. It mainly includes the following layers:

Application Framework

The Application Framework separates the entire operating system into two parts. For Application developers, all apps run on the Application Framework, and do not need to care about the underlying system. The Application Framework layer provides a wide range of Application programming interfaces for Application developers, such as Activity Manager, Content Provider, Notification Manager, and various Window Widget resources. In the Application Framework layer, Activity is the most basic component of an APP. Generally, each Activity corresponds to a view (or screen) on the screen. An APP can have one or more activities. The application is packaged into A. apk file, which is interpreted and executed by Dalvik VM.

Dalvik VM

The Dalvik virtual machine uses the register architecture instead of the JVM stack structure. The. class file compiled by the Java program cannot be interpreted and executed in Dalvik. Therefore, Google provides a dx tool to convert. class files into A. dex format that can be recognized by Dalivk. The specific Dalvik VM details are not the focus of this article, and will not be discussed below.

Linux kernel

As described above, all apps are written in Java code and interpreted and executed in Dalvik VM. In the Android operating system, each Instance of each Dalvik VM corresponds to a process in the Linux kernel. You can use the adb shell tool to view the current process in the system. As shown in, the list of processes in the kernel after Android2.3.3 is started.


Each item marked as app_xx is a process occupied by an app. It can be seen that the Android design enables each application to be interpreted and executed by an independent Dalvik instance, each Linux kernel process loads a Dalvik instance to provide the app runtime environment in this way. In this way, the resources of each APP are completely blocked and do not interfere with each other. Although the difficulties of inter-process communication are introduced at the same time, it also brings stronger security.

Android memory reclaim Principle

The following describes the Resource Management Mechanism of the Android operating system at the Application Framework and Linux kernel levels.
Android uses a special resource management mechanism because it was designed for mobile terminals at the beginning. All available memory is limited to system RAM, and corresponding optimization solutions must be designed for this limitation. When the Android Application exits, it does not clean up the memory occupied by it, and the Linux kernel process also exists accordingly. The so-called "exit but not close ". This allows the user to get a response immediately when calling the program. When the system memory is insufficient, the system will activate the memory recovery process. In order not to affect the user experience (such as killing the current active process) due to memory recycle, Android sets the default five recycle priorities based on the components running in the process and their statuses:

IMPORTANCE_FOREGROUND:IMPORTANCE_VISIBLE:IMPORTANCE_SERVICE:IMPORTANCE_BACKGROUND:IMPORTANCE_EMPTY:

The recovery sequence of these priorities is Empty process, Background process, Service process, Visible process, and Foreground process. For details about the partitioning principles, see http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html.
ActivityManagerService centrally manages the memory resource allocation of all processes. All processes must call the ActivityManagerService object before applying for or releasing the memory. After obtaining the "License", the ActivityManagerService can perform the next operation ". Several important member methods involved in memory reclaim in ActivityManagerService are as follows: trimApplications (), updateOomAdjLocked (), and activityIdleInternal (). These member methods are mainly responsible for the default memory recycle mechanism of Android. If the memory recycle mechanism in the Linux kernel is not disabled, the default recycle mechanism is skipped.

Default Recycle Process

Memory recovery in the Android operating system can be divided into two levels: Default memory recovery and kernel-level memory recovery. This chapter mainly studies the default memory recovery mechanism, the memory recovery mechanism at the Linux kernel level will be introduced in the next section. For all the code in this chapter, see ActivityManagerService. java.

Recycling action entry: activityIdleInternal ()

Memory recovery trigger points in the Android system can be roughly divided into three situations. First, the user program calls StartActivity () to overwrite the Activity of the current Activity. Second, the user presses the back key to exit the current application. Third, start a new application. The final function interface that can trigger memory reclaim is activityIdleInternal (). When ActivityManagerService receives the asynchronous message IDLE_TIMEOUT_MSG or IDLE_NOW_MSG, activityIdleInternal () is called. The Code is as follows:

Listing 1. IDLE_NOW_MSG Processing Method
case IDLE_NOW_MSG:{  IBinder token = (Ibinder)msg.obj;     activityIdle(token, null);  }  break;
Listing 2. IDLE_TIMEOUT_MSG Processing Method
case IDLE_TIMEOUT_MSG: {  if (mDidDexOpt) {         mDidDexOpt = false;         Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);         nmsg.obj = msg.obj;         mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);         return;     }     IBinder token = (IBinder)msg.obj;     Slog.w(TAG, "Activity idle timeout for " + token);     activityIdleInternal(token, true, null);  }  break;

IDLE_NOW_MSG is triggered by events such as Activity switching and Activiy focus change. IDLE_TIMEOUT_MSG is triggered when Activity startup times out. Generally, this timeout time is set to 10 s, if an Activity is not started successfully within 10 s, the asynchronous message IDLE_TIMEOUT_MSG will be sent to recycle resources. The main task of activityIdleInternal () is to change the status information of the Activity in the system and add it to different status lists. Its main work is as follows:
Call the scheduleAppGcsLocked () method to notify all ongoing tasks of garbage collection. ScheduleAppGcsLocked () is used to schedule the garbage collect of JVM and reclaim part of the memory space. Here, it only notifies each process of spam check and scheduling collection time, rather than synchronous collection. Then, retrieve all the content in the mStoppingActivities and mFinishigActivities lists and store them in temporary variables. The two lists store activity objects in the stop and finishi statuses respectively. For the stop list, if the finish status of the activity is true, determine whether to stop immediately. If you want to stop immediately, call destroyActivityLocked () to notify the target process to call the onDestroy () method. Otherwise, call resumeTopActivity () to run the next Activity. If the finish status is false, stopActivityLocked () is called to notify the client process to stop the Activity. This usually happens after startActivity () is called. For the finish list, call destroyActivityLocked () to notify the client process to destroy the target Activity.
Here, the destroyActivityLocked and other functions do not actually change the memory usage, but change its status to "allow recycling". The actual recycling is in the trimApplications () function to be called below.

Recycling process function trimApplications ()

The structure of the trimApplications () function is as follows:

Listing 3. trimApplications Function
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     }  }

The positions of the three serial numbers in listing 3 are responsible for the following:
(1) After the program is executed to trimApplications (), first check the processes in the mRemovedProcesses list. The mRemovedProcesses list mainly contains the crash process, the process that does not respond within 5 seconds and is selected by the user for forced shutdown, and the process that the Application Development calls killBackgroundProcess to kill. Call Process. killProcess to kill all such processes.
(2) Call the updateOomAdjLocked () function. If the response is successful, the Linux kernel supports the setOomAdj () interface. updateOomAdjLocked will modify the value of the adj and notify the linux kernel, the kernel dynamically manages process resources (lowmemorykiller and oom_killer) based on the embedded values and memory usage ). If updateOomAdjLocked () returns false, it indicates that the current system does not support the setOomAdj () interface, and the default resource is recycled locally.
(3) At last, if too many activities are still running, recycle unnecessary activities. Most code of trimApplications () processes the default resource reclaim when Oom_killer does not exist, and faces the default recycle process (that is, the location marked (2) in the code list) for further analysis. The recycling process can be roughly described as follows.
Step 1: obtain all currently running processes mLruProcesses. The sorting rules in mLruProcesses are based on the recent usage time. Count the processes that cannot be shut down in mLruProcesses. The processes that cannot be shut down include the processes that run the service and the processes that run the broadcast explorer. See the following code.

Listing 4. Count processes that cannot be disabled
if (app.persistent || app.services.size() != 0             || app.curReceiver != null             || app.persistentActivities > 0) {  // Don't count processes holding services against our     // maximum process count.         numServiceProcs++;  }

Step 2: set the current maximum number of processes to curMaxProcs = curMaxProcs + numServiceProcs (that is, the sum of the default maximum number of processes and the number of processes running the Service), if the current number of processes is mRemovedProcesses. if the size () value is greater than this value, all processes that are currently running are traversed to kill qualified processes and release the memory. For the cleaning process, see listing 5 (some code is omitted ). From the code in listing 5, we can see that the process is killed by the following conditions:
① It must be a non-persistent process, that is, a non-system process;
② It must be an empty process, that is, no activity exists in the process. If the active process is killed, it is possible to close the program in use or increase the latency of application recovery, thus affecting the user experience;
③ No broadcast receiver is required. Generally, running a broadcast receiver is waiting for an event. You do not want this program to be forcibly disabled by the system;
④ The number of services in the process must be 0. A service process is likely to provide certain services for one or more programs, such as the GPS positioning service. Killing such processes will make other processes unable to serve normally.
The preceding conditions are indispensable.

Listing 5. Cleaning Process
 if (!app.persistent && app.activities.size() == 0             && app.curReceiver == null && app.services.size() == 0) {         if (app.pid > 0 && app.pid != MY_PID) {             Process.killProcess(app.pid);         } else {             try {                 app.thread.scheduleExit();             } catch (Exception e) {                 // Ignore exceptions.             }         }         // todo: For now we assume the application is not buggy         // or evil, and will quit as a result of our request.         // Eventually we need to drive this off of the death         // notification, and kill the process if it takes too long.         cleanUpApplicationRecordLocked(app, false, i);         i--;  }

Step 3: Check the currently running process again. If mRemovedProcesses. size () is still greater than curMaxProcs, the condition is relaxed and recycled again. For the judgment conditions, see code listing 6 (some code is omitted ). In the following code, when the value of the Boolean variable canQuit is true, this process can be recycled. The value of canQuit is divided into two steps. The first step is to assign values based on the attributes of the process. 1. It must be a non-persistent process, that is, a non-system process; 2. There must be no broadcast guest er; 3. The number of services in the process must be 0; 4. The number of persistent activities is 0. The only difference between Step 2 and Step 2 is Article 4th. Here, the process is not required to be a null process, as long as there is no Activity of the persistent type in the process (whether the Activity is specified in the development stage ). When these conditions are met, check the attributes of each Activity in the process. When all the activities in the process must meet three conditions: the Activity status has been saved, the Activity is currently invisible and has been stopped. At this time, killing the process will only reduce the loading speed of the next program call. The next time the process is started, it will be restored to the State before it is closed, and will not cause a fatal impact on the user experience, canQuit is set to true. This situation is different from the recycling method in step 2. because the number of activities in the process is not 0, you need to execute destroyActivityLocked () to destroy each Activity and finally kill the process.

Listing 6. Execute destroyActivityLocked () to destroy
boolean canQuit = !app.persistent && app.curReceiver == null  && app.services.size() == 0     && app.persistentActivities == 0;  int NUMA = app.activities.size();  for (j=0; j
  
    0 && app.pid != MY_PID) {         Process.killProcess(app.pid);     }     cleanUpApplicationRecordLocked(app, false, i);     i--;     //dump();  }
  

Step 4: The above three processes are all resources for the entire process. After the preceding process is completed, resources of the Activity will be recycled at a smaller granularity. Similar to the above, the list of mLRUActivities stores all currently running activities, and the sorting rules are also the principle of least access. MLRUActivities. size () returns the number of activities running in the system. When the value is greater than MAX_ACTIVITIES (MAX_ACTIVITIES is a constant with a general value of 20, representing the maximum number of activities allowed to exist simultaneously in the system. Partial Activity that meets the condition will be recycled to reduce memory usage. The code listing 7 shows the recycling condition:

Listing 7. Recycling condition code
//Finally, if there are too many activities now running, try to  // finish as many as we can to get back down to the limit.  for (   i=0;         i
  
    curMaxActivities;         i++) {  final HistoryRecord r         = (HistoryRecord)mLRUActivities.get(i);     // We can finish this one if we have its icicle saved and     // it is not persistent.     if ((r.haveState || !r.stateNotNeeded) && !r.visible             && r.stopped && !r.persistent && !r.finishing) {         final int origSize = mLRUActivities.size();         destroyActivityLocked(r, true);         if (origSize  > mLRUActivities.size()) {             i--;         }     }  }
  

Here, only the memory resources of the Activity are recycled, and the process will not be killed or affected. When a process needs to call a killed Activity, it can reply from the saved status. Of course, it may require a relatively long latency.
Back to Top

Linux Kernel memory reclaim lowmemorykiller

As mentioned above, the trimApplications () function executes a function called updateOomAdjLocked (). If false is returned, the default function is recycled. If true is returned, the default memory is recycled. UpdateOomAdjLocked updates a variable named adj for each process and informs the Linux kernel that the kernel maintains an embedded data structure (that is, the progress table ), lowmemorykiller is used to check the system memory usage. When the memory is insufficient, some processes are killed and the memory is released. The following describes the memory recovery mechanism that works with the Android Framework and Linux kernel.
Because all applications in the Android operating system run in an independent Dalvik virtual machine environment, the Linux kernel cannot know the running status of each process, and thus cannot maintain a proper value for each process, therefore, the Android Application Framework must provide a set of mechanisms to dynamically update the adj of each process. This is updateOomAdjLocked ().
UpdateOomAdjLocked () is located in ActivityManagerService. Its main function is to select an appropriate adj value for the process and notify the Linux kernel to update this value. UpdateOomAdjLocked first calls computeOomAdjLocked () to preliminarily calculate the value of adj, and then returns to updateOomAdjLocked () to further modify the value. For the estimation process, see the code.
For more information, see task_struct-> signal_struct-> adj, file/kernel/include/linux/sched. h. Essentially a variable in the process data structure, used to indicate the priority order of killing the process when Out of Memory occurs. Lowmemorykiller uses this variable to determine the importance of the process and releases some space when the memory is insufficient. In fact, it is in the file/kernel/drivers/staging/android/lowmemorykiller. c. Lowmemorykiller defines two Arrays: lowmem_adj and lowmem_minfree. Lowmen_adj defines a series of adj key values, and each element of lowmem_minfree represents a memory threshold. In the following code, the four thresholds are 6 MB, 8 MB, 16 MB, and 64 MB, indicating that when the memory is smaller than 64 MB, processes with a value greater than or equal to 12 will be killed and recycled. If the memory is less than 16 MB, those with a value greater than or equal to 6 will be killed and recycled. If the memory is less than 8 MB, processes with a value greater than or equal to 1 will be killed and recycled. If the memory is less than 6 MB, all processes with a value greater than or equal to 0 will be killed and recycled. Each process in the kernel holds an adj value ranging from-17 to 15. The smaller the value, the higher the importance of the process and the lower the recycle priority. Among them,-17 indicates disabling automatic recycle. In Android, only 0-15 is used.

Listing 8. Each process has
static int lowmem_adj[6] = {         0,         1,         6,         12,  };  static int lowmem_adj_size = 4;  static size_t lowmem_minfree[6] = {         3 * 512,      /* 6MB */         2 * 1024,     /* 8MB */         4 * 1024,     /* 16MB */         16 * 1024,    /* 64MB */  };  static int lowmem_minfree_size = 4;

Lowmemorykiller registers a lowmem_shrinker. lowmem_shrinker uses the Cache Shrinker in the standard Linux kernel. When the idle Memory Page is insufficient, the kernel thread kswapd uses the registered lowmem_shrinker to recycle the Memory Page.

Listing 9. Use the registered lowmem_shrinker to reclaim the Memory Page
static struct shrinker lowmem_shrinker = {                     .shrink = lowmem_shrink,         .seeks = DEFAULT_SEEKS * 16  };  static int __init lowmem_init(void)  {         task_free_register(&task_nb);         register_shrinker(&lowmem_shrinker);         return 0;  }

The lowmem_shrink code is in the lowmem_shrink function. The main structure of this function is given below. Lowmem_shrink traverses all processes according to the above rules, selects the processes to be terminated, and forces these processes to end by sending an unnegligible signal SIGKILL

Listing 10. Force Process Termination

Static int lowmem_shrink (struct shrinker * s, int nr_to_scan, gfp_t gfp_mask)
{
For_each_process (p ){
// Select processes to be forced
}
If (selected ){
Force_sig (SIGKILL, selected );
Rem-= selected_tasksize;
} Else
Rem =-1;
Return rem;
}

Oom_killer.

If the above methods cannot release enough Memory space, an Out of Memory exception will occur when an application is allocated to a new process, OOM_killer will do his best to kill some processes to release space. OOM_killer in Android inherits from the standard Linux 2.6 kernel and is used to process Out of Memory when Memory is allocated. Android has not modified its implementation method. Its location is linux/mm/oom_kill.c. Oom_killer traverses the process and calculates the badness value of all processes. Select the process with the largest badness to kill it. The Declaration of the function badness is as follows:
Unsigned long badness (struct task_struct * p, unsigned long uptime) function select_bad_process returns the process to be killed.

Listing 11. Return the process to be killed
static struct task_struct *select_bad_process(unsigned long *ppoints,                                             struct mem_cgroup *mem)  {         for_each_process(p) {                points = badness(p, uptime.tv_sec);                if (points > *ppoints || !chosen) {                        chosen = p;                        *ppoints = points;                }         }         return chosen;  }

Finally, like lowmemorykiller, the selected process is terminated by sending SIGKILL. Since oom_killer is no different from the standard Linux kernel, we will not study it in detail here.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.