Android memory management is slightly different from linux. One of them is the introduction of Low memory killer.
1. Reason for introduction
Android is a multitasking system, that is, it can run multiple programs at the same time, which should be familiar to everyone. Generally, it takes some time to start and run a program. Therefore, to speed up running, Android will not immediately kill a program when you exit it, in this way, the program can be started soon when you run the program again. As more programs are retained in the system, the memory will be insufficient. low memory killer is used to clear related programs when the system memory is lower than a certain value, ensure that the system maintains a certain amount of idle memory.
In Android, the lifecycle of a process is controlled by the system. Even if the user closes the program, the process still exists in the memory. This design aims to enable quick start next time. Of course, as the system running time increases, the memory will decrease. Android Kernel periodically performs a check to kill some processes and release the memory.
So how can we determine which processes need to be killed? The answer is our title: Low memory killer mechanism.
Android's Low memory killer is improved based on the out of memory rules of linux. OOM uses some complex scoring mechanisms to rate processes, and then determines processes with high scores as bad processes, killing and releasing memory. OOM checks are started only when the system memory is insufficient, while Low memory killer checks regularly.
Low memory killer is mainly used to determine the importance of a process through oom_adj of the process. The size of oom_adj is related to the process type and the order in which the process is scheduled.
For more information about the implementation of Low memory killer, see kernel/drivers/misc/lowmemorykiller. c.
The principle is very simple. in linux, there is a kswapd kernel thread. When linux recycles and stores pages, the kswapd thread will traverse a shrinker linked list and execute a callback, which is defined as follows:
Struct shrinker {
Int (* shrink) (int nr_to_scan, gfp_t gfp_mask );
Int seeks;
Struct list_head list;
Long nr;
};
# Define DEFAULT_SEEKS 2
Extern void register_shrinker (struct shrinker *);
Extern void unregiter_shrinker (struct shrinker *);
So as long as you register Shrinker, you can release the memory according to the rule when the memory is recycled by page. Let's take a look at its implementation.
First, define the shrinker struct. lowmem_shrink is the pointer to the callback function. When memory is recycled by PAGE, this function will be called.
Static struct shrinker lowmem_shrinker = {
. Shrink = lowmem_shrink,
. Seeks = DEFAULT_SEEKS * 16
};
2. Basic Principles and important concepts
Low memory killer determines the process to be released based on two principles: the importance of the process and the amount of idle memory that can be acquired by the process.
(1) The importance of a process is determined by task_struct-> signal_struct-> oom_adj.
Android divides programs into the following categories, which are in descending order of importance:
Description of name oom_adj
FOREGROUD_APP 0 foreground program, which can be understood as the program you are using
VISIBLE_APP 1 user-visible Program
SECONDARY_SERVER 2 backend service. For example, QQ will run the service on the backend.
HOME_APP 4 HOME is the main interface
HIDDEN_APP 7 hidden programs
CONTENT_PROVIDER 14 content provider,
EMPTY_APP 15 is an empty program that neither provides services nor content.
Each program has an oom_adj value. The smaller the value, the more important the program is, and the lower the possibility of being killed.
(2) The process memory is obtained through get_mm_rss. In the same oom_adj, if the memory is large, it is killed first.
(3) When is the memory low? What happens when low memory killer starts working? Android provides two Arrays: lowmem_adj and lowmem_minfree. The former stores the oom_adj threshold value, and the latter stores the minfree warning value, in page (4 K ).
Oom_adj memory alert value (in 4 K)
0 1536
1 2048
2 4096
7 5120
14 5632
15 6144
3. source code parsing
Module_init (lowmem_init );
Module_exit (lowmem_exit );
The main functions of the module are the register_shrinker and unregister_shrinker struct lowmem_shrinker. It registers the lowmem_shrink function to the shrinker linked list and calls it in mm_scan.
This function is described in detail below:
For (I = 0; I <array_size; I ++ ){
If (other_file <lowmem_minfree [I]) {
Min_adj = lowmem_adj [I];
Break;
}
}
Other_file: the number of idle memory in the system. Based on the logic above, it is determined that low memory killer needs to analyze whether or not to release processes with a value higher than (min_adj.
If (nr_to_scan <= 0 | min_adj = OOM_ADJUST_MAX + 1 ){
Lowmem_print (5, "lowmem_shrink % d, % x, return % d \ n ",
Nr_to_scan, gfp_mask, rem );
Return rem;
}
Determines whether the current system status requires low memory killer.
For_each_process (p ){
Struct mm_struct * mm;
Struct signal_struct * sig;
Int oom_adj;
Task_lock (p );
Mm = p-> mm;
Sig = p-> signal;
If (! Mm |! Sig ){
Task_unlock (p );
Continue;
}
Oom_adj = sig-> oom_adj;
If (oom_adj <min_adj ){
Task_unlock (p );
Continue;
}
Tasksize = get_mm_rss (mm );
Task_unlock (p );
If (tasksize <= 0)
Continue;
If (selected ){
If (oom_adj <selected_oom_adj)
Continue;
If (oom_adj = selected_oom_adj &&
Tasksize <= selected_tasksize)
Continue;
}
Selected = p;
Selected_tasksize = tasksize;
Selected_oom_adj = oom_adj;
Lowmem_print (2, "select % d (% s), adj % d, size % d, to kill \ n ",
P-> pid, p-> comm, oom_adj, tasksize );
}
For a process whose sig-> oom_adj is greater than min_adj, find the process that occupies the largest memory and store it in selected.
If (selected ){
If (fatal_signal_pending (selected )){
Pr_warning ("process % d is suffering a slow death \ n ",
Selected-> pid );
Read_unlock (& tasklist_lock );
Return rem;
}
Lowmem_print (1, "send sigkill to % d (% s), adj % d, size % d \ n ",
Selected-> pid, selected-> comm,
Selected_oom_adj, selected_tasksize );
Force_sig (SIGKILL, selected );
Rem-= selected_tasksize;
}
Send the SIGKILL message to kill the process.
The analysis of Low Memory Killer is here. After learning about its mechanism and principle, we find that its implementation is very simple, similar to the standard Linux OOM mechanism, the implementation method is slightly different. The OOM Killer mechanism in standard Linux is implemented in mm/oom_kill.c and will be called by _ alloc_pages_may_oom (in mm/page_alloc.c when memory is allocated ). Oom_kill.c the main function is out_of_memory, which selects a bad process Kill. The Kill method also sends a SIGKILL signal. In out_of_memory, you can call select_bad_process to select a process Kill. The selection is implemented in the badness function and each process is scored based on multiple criteria. The most rated process is selected and killed. Generally, the more memory is occupied, the larger oom_adj is, and the more likely it is to be selected.
4. Resource Configuration
The threshold table can be configured through/sys/module/lowmemorykiller/parameters/adj and/sys/module/lowmemorykiller/parameters/minfree. For example, in init. rc:
# Write value must be consistent with the above properties.
Write/sys/module/lowmemorykiller/parameters/adj
Write/proc/sys/vm/overcommit_memory 1
Write/sys/module/lowmemorykiller/parameters/minfree 1536,2048, 4096,5120, 5632,6144
Class_start default
The process oom_adj can also be set in the init. in rc, the pid of the init process is 1, omm_adj is configured as-16, and will never be killed.
# Set init its forked children's oom_adj.
Write/proc/1/oom_adj-16
The basic principle of Low memory killer should be clarified. As I mentioned earlier, the size of the Process omm_adj is related to the process type and the order in which the process is scheduled. The process type can be clearly seen in ActivityManagerService:
Static final int EMPTY_APP_ADJ;
Static final int HIDDEN_APP_MAX_ADJ;
Static final int HIDDEN_APP_MIN_ADJ;
Static final int HOME_APP_ADJ;
Static final int BACKUP_APP_ADJ;
Static final int SECONDARY_SERVER_ADJ;
Static final int HEAVY_WEIGHT_APP_ADJ;
Static final int PERCEPTIBLE_APP_ADJ;
Static final int VISIBLE_APP_ADJ;
Static final int FOREGROUND_APP_ADJ;
Static final int CORE_SERVER_ADJ =-12;
Static final int SYSTEM_ADJ =-16;
ActivityManagerService defines oom_adj of various processes. CORE_SERVER_ADJ represents the omm_adj of some core services. The value is-12. It can be seen from the previous analysis that such processes will never be killed.
Other unassigned values are initialized in the static block and configured through system/rootdir/init. rc:
In init. rc:
# Define the oom_adj values for the classes of processes that can be
# Killed by the kernel. These are used in ActivityManagerService.
Setprop ro. FOREGROUND_APP_ADJ 0
Setprop ro. VISIBLE_APP_ADJ 1
Setprop ro. SECONDARY_SERVER_ADJ 2
Setprop ro. HIDDEN_APP_MIN_ADJ 7
Setprop ro. CONTENT_PROVIDER_ADJ 14
Setprop ro. EMPTY_APP_ADJ 15
# Define the memory thresholds at which the above process classes will
# Be killed. These numbers are in pages (4 k ).
Setprop ro. FOREGROUND_APP_MEM 1536
Setprop ro. VISIBLE_APP_MEM 2048
Setprop ro. SECONDARY_SERVER_MEM 4096
Setprop ro. HIDDEN_APP_MEM 5120
Setprop ro. CONTENT_PROVIDER_MEM 5632
Setprop ro. EMPTY_APP_MEM 6144
As a result, we know that EMPTY_APP is most likely to be killed, but it is actually CONTENT_PROVIDER, And the FOREGROUND process is hard to be killed.
Now let's talk about the scheduling order of the process, the second factor that affects oom_adj. This involves the complex scheduling of ActivityManagerService. Let's take a look at it next time.
Postscript:
Android divides processes into six levels. The order of priority is from high to low:
1. FOREGROUND_APP:
This is the process running the current foreground app. We 'd really rather not kill it!
The program that the user is using. this setting is too high. The user sees that a program in use disappears inexplicably and then automatically returns to the desktop .. (because it is killed by the system ..) so it is best not to touch it.
2. VISIBLE_APP:
This is a process only hosting activities that are visible to the user, so we 'd prefer they don't disappear.
Similar to FOREGROUND_APP, the user is using/to see it. the difference between them is that VISIBLE_APP may not be a user focus program, but the user can see it, or it does not cover the whole screen, only part of the screen. so it can be a little higher than FOREGROUND_APP.
3. SECONDARY_SERVER:
This is a process holding a secondary server -- killing it will not have much of an impact as far as the user is concerned.
Services of all applications. System-level services such as PhoneService do not belong to this category, and they will never be terminated by Android. Therefore, this can be set to a higher level ~ Note that the HOME (SenseUI) is also included here, so do not set it too high. Otherwise, you have to wait for it to load again every time you return to the desktop, especially if there are many widgets.
4. HIDDEN_APP:
This is a process only hosting activities that are not visible, so it can be killed without any disruption.
A program originally belongs to 1 or 2. After the user presses "back" or "home", the program itself cannot be seen, but the programs that are still running actually belong to HIDDEN_APP. there is no impact on killing .. but it should be noted that not all of them should end immediately, such as push mail, locale, alarm clock, and so on. therefore, do not set too high. the Program (a really useless program) that "should" exit with a few return keys is below.
5. CONTENT_PROVIDER:
This is a process with a content provider that does not have any clients attached to it. If it did have any clients, its adjustment wocould be the one for the highest-priority of those processes.
The difference between 5 and 6 is not very well understood. This is also of little use, but it is still a little bit more useful than EMPTY_APP .. so it doesn't matter if it is a high point ~,
6. EMPTY_APP:
This is a process without anything currently running in it. Definitely the first to go! This value is initalized in the constructor, careful when refering to this static variable externally.
It's totally useless. It's only good to kill it. It's the first thing to do!
The configuration is set in the file: "/sys/module/lowmemorykiller/parameters/minfree"
You can view the current settings:
1. # cat/sys/module/lowmemorykiller/parameters/minfree
Six numbers are displayed, separated by commas (,). For example:
1536,2048, 4096,5120, 5632,6144
Note that the unit of these numbers is page. 1 page = 4 kilobyte.
The above six numbers correspond to (MB): 6, 8, 16, 20
These numbers are the corresponding memory threshold values. Once they are lower than this value, Android starts to close the Process in order. therefore, Android begins to end EMPTY_APP with the lowest priority when the available memory is less than 24 MB (6144*4/1024 ).
I don't know where the available memory is obtained. Obviously, it is not the free display of available memory, and it seems that compcache and swap do not affect it.
To reset this value:
1. # echo "1536,2048, 4096,5120, 15360,23040">/sys/module/lowmemorykiller/parameters/minfree;
In this way, when the available memory is less than 90 MB, The EMPTY_APP will be ended. When the available memory is less than 60 MB, The CONTENT_PROVIDER group will be ended. The remaining four remain unchanged.
Note:
The value changed through the above method is not permanent. After the next restart, It is restored to the previous setting. If you want the setting to be executed at each boot;
Echo "1536,3072, 4096,21000, 23000,25000">/sys/module/lowmemorykiller/parameters/minfree
Add it to any configuration file that starts from startup. generally in/system/init. all files under d are run at startup (some ROM may not be here ..) you only need to open any file in notepad and add this line to it.
Personal experience:
Generally, the first three do not need to be moved, or just change a little higher. After all, they are important and should not end too early. the last three items are worth noting. they can also be set to a larger value. of course, everything has a degree, as in the above example, EMPTY_APP gets 25000 (97 MB) is not as big as it is, hero I did not have it I even tried 120MB in Magic 32A .. but MB is a little overhead .. when the game is playing, it disappears and returns to the desktop (the available memory is less than MB and the task is completed by the system ).. therefore, it should not be too high.
My current settings (with 32 mb cc and 32 MB backing swap @ 60 swappiness enabled ):
1536,2048, 4096,8192, 11520,19200
You may not understand the benefits of doing so. the advantage is that we can have enough memory to execute the program we want to run at any time, and those truly useless processes will not occupy valuable memory. more importantly, you do not need to participate or assist any third-party software, and Android will automatically execute this operation in the background. imagine who is more familiar with every process than Android? It's much more intelligent than the one-stop method of memory management programs ~ Let's Delete the memory management programs from now on.