1 low memory killer
Android的lowmemory killer是基于linux的OOM(out ofmemory)规则改进而来的。OOM通过一些比较复杂的评分机制,对运行进程进行打分,然后将分数高的进程判定为bad进程,杀死进程并释放内存。OOM只有当系统内存不足的时候才会启动检查,而lowmemory killer则不仅是在应用程序分配内存发现内存不足时启动检查,它也会定时地进行检查。
Low memory killer主要是通过进程的oom_adj来判定进程的重要程度的。oom_adj的大小和进程的类型以及进程被调度的次序有关。
Low memory killer的具体实现在kernel,比如对于androidL,代码在:/local/sourcecode/AndroidLSoul45/kernel-3.10/drivers/staging/android/lowmemorykiller.c。由于是根据MTK释放的androidL源码来研究,里面难免有相当篇幅是MTK自己添加的,目的是定制mtk自己memroy优化方案。本文介绍的时候可能会忽略这一部分代码,回归android原生系统。
1.1 基本原理
Low Memory Killer与OOM的区别:OOM即Out ofMemory是标准linuxKernel的一种内存管理机制,LowMemory Killer在它基础上作了改进:OOM基于多个标准给每个进程打分,分最高的进程将被杀死;LowMemory Killer则用oom_adj和占用内存的大小来选择Bad进程,OOM在内存分配不足时调用,而LowMemory Killer每隔一段时间就会检查,一旦发现空闲内存低于某个阈值,则杀死Bad进程。
基本实现原理:在linux中,存在一个名为kswapd的内核线程,当linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,或者某个app分配内存,发现可用内存不足时,则内核会阻塞请求分配内存的进程分配内存的过程,并在该进程中去执行lowmemorykiller来释放内存。(具体kswapd怎么工作,没有研究)。
结构体shrinker的定义在AndroidLSoul45/kernel-3.10/include/linux/shrinker.h,具体如下:
struct shrinker {
int (*shrink)(struct shrinker *, structshrink_control *sc);
int seeks; /* seeks to recreate an obj */
long batch; /* reclaim batch size, 0 = default */
/* These are for internal use */
struct list_head list;
atomic_long_t nr_in_batch; /* objspending delete */
};
#defineDEFAULT_SEEKS 2 /* A good number if you don't know better. */
extern voidregister_shrinker(struct shrinker *);
extern void unregister_shrinker(structshrinker *);
#endif
根据上面的代码逻辑可以看出,只要注册shrinker,就可以在内存分页回收时根据规则释放内存。下面具体看看lowmemorykiller.c里是怎么实现的。
1.2 lowmemorykiller实现机制
下面具体介绍lowmemorykiller的原理。
1.2.1初始化与注销
//module_init(lowmem_init);android4.4采用这种方式
late_initcall(lowmem_init);
module_exit(lowmem_exit);
模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里。初始化模块时进行注册,模块退出时注销。
static structshrinker lowmem_shrinker = {
.shrink = lowmem_shrink,//函数指针
.seeks = DEFAULT_SEEKS * 16
};
//注册
static int __initlowmem_init(void)
{
#ifdef CONFIG_ZRAM
vm_swappiness = 100;
#endif
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
return 0;
}
//注销
static void __exitlowmem_exit(void)
{
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
}
当然这里进行初始化和注销工作的前提是先定义shrinker结构体,lowmem_shrink为回调函数的指针,当有内存分页回收的时候,获其他有需要的时机,这个函数将会被调用。
1.2.2阀值表
Android中,存在着一张内存阈值表,这张阈值表是可以在init.rc中进行配置的,合理配置这张表,对于小内存设备有非常重要的作用。我们来看lowmemorykiller.c中这张默认的阈值表:
static intlowmem_adj[6] = {
0,
1,
6,
12,
};
static intlowmem_adj_size = 4;
static intlowmem_minfree[6] = {
3 * 512, /*6MB */
2 * 1024, /*8MB */
4 * 1024, /*16MB */
16 * 1024, /* 64MB */
};
lowmem_adj[6]中各项数值代表阈值的警戒级数;lowmem_minfree[6]代表对应级数的剩余内存。也就是说,当系统的可用内存小于6MB时,警戒级数为0;当系统可用内存小于8M而大于6M时,警戒级数为1;当可用内存小于64M大于16MB时,警戒级数为12。Low memory killer的规则就是根据当前系统的可用内存多少来获取当前的警戒级数,如果进程的oom_adj大于警戒级数并且最大,进程将会被杀死(具有相同oom_adj的进程,则杀死占用内存较多的)。oom_adj越小,代表进程越重要。一些前台的进程,oom_adj会比较小,而后台的服务,oom_adj会比较大,所以当内存不足的时候,Low memory killer必然先杀掉的是后台服务而不是前台的进程。
1.2.3回调函数lowmem_shrink
staticint lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
struct task_struct *selected = NULL;
int rem = 0;
int tasksize;
int i;
int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int selected_tasksize = 0;
int selected_oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
//这部分通过global_page_state()函数获取系统当前可用的内存大小,然后根据可用内存大小及内存阈值表,来计算系统当前的警戒等级。
int other_free =global_page_state(NR_FREE_PAGES) - totalreserve_pages;
int other_file =global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM);
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
for (i = 0; i < array_size; i++) {
if (other_free < lowmem_minfree[i]&&
other_file < lowmem_minfree[i]){
min_score_adj = lowmem_adj[i];
break;
}
}
if (sc->nr_to_scan > 0)
lowmem_print(3, "lowmem_shrink%lu, %x, ofree %d %d, ma %d\n",
sc->nr_to_scan, sc->gfp_mask,other_free,
other_file, min_score_adj);
//计算rem值
rem =global_page_state(NR_ACTIVE_ANON) +
global_page_state(NR_ACTIVE_FILE) +
global_page_state(NR_INACTIVE_ANON) +
global_page_state(NR_INACTIVE_FILE);
//如果内存状态还是不错,则返回rem,不做其他操作
if(sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_shrink%lu, %x, return %d\n",
sc->nr_to_scan,sc->gfp_mask, rem);
returnrem;
}
//****下面开始遍历系统中所有的进程,选中将要杀掉的那个进程***
selected_oom_score_adj= min_score_adj;
rcu_read_lock();
for_each_process(tsk) {
struct task_struct *p;
int oom_score_adj;
//跳过内核线程,内核线程不参加这个杀进程
if (tsk->flags & PF_KTHREAD)
continue;
p = find_lock_task_mm(tsk);//有mm的thread才能操作
if (!p)
continue;
if (test_tsk_thread_flag(p, TIF_MEMDIE)&&
time_before_eq(jiffies,lowmem_deathpending_timeout)) {
task_unlock(p);
rcu_read_unlock();
return 0;
}
oom_score_adj =p->signal->oom_score_adj;// 得到task对应的oom_score_adj
//进程的oom_score_adj小于警戒阈值,则不处理
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
//获取进程所占用的内存大小,RSS值
tasksize =get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
//如果这个进程的oom_score_adj小于我们已经选中的那个进程的oom_score_adj,或者这个进程的oom_score_adj等于我们已经选中的那个进程的oom_score_adj,但其所占用的内存大小tasksize小于我们已经选中的那个进程所占用内存大小,则继续寻找下一个进程。
if (selected) {
if (oom_score_adj <selected_oom_score_adj)
continue;
if (oom_score_adj ==selected_oom_score_adj &&
tasksize <=selected_tasksize)
continue;
}
//选中正在遍历的这个的进程,更新selected_tasksize为这个进程所占用的内存大小tasksize,更新selected_oom_score_adj为这个进程的oom_score_adj。
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
lowmem_print(2, "select %d (%s),adj %d, size %d, to kill\n",
p->pid, p->comm,oom_score_adj, tasksize);
}
//*************遍历结束***********************
//杀死选中的进程。先是更新lowmem_deathpending_timeout,然后便是给进程发送一个signalSIGKILL,接下来是设置进程的标记为TIF_MEMDIE,最后则是更新一下rem的值。
if (selected) {
lowmem_print(1, "send sigkill to%d (%s), adj %d, size %d\n",
selected->pid, selected->comm,
selected_oom_score_adj,selected_tasksize);
lowmem_deathpending_timeout = jiffies +HZ;
send_sig(SIGKILL, selected, 0);
set_tsk_thread_flag(selected,TIF_MEMDIE);
rem -= selected_tasksize;
}
lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
sc->nr_to_scan, sc->gfp_mask,rem);
rcu_read_unlock();
return rem;
}
1.2.4 adj阀值设置
前面提到lowmemorykiller中有一个默认的阈值表。如我们前面所提到的那样,各种各样的设备、产品也可以自己定制这个值。这种定制主要是通过导出一些内核变量来实现的。
#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
static shortlowmem_oom_adj_to_oom_score_adj(short oom_adj)
{
if (oom_adj == OOM_ADJUST_MAX)
return OOM_SCORE_ADJ_MAX;
else
return (oom_adj *OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
}
static voidlowmem_autodetect_oom_adj_values(void)
{
int i;
short oom_adj;
short oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (array_size <= 0)
return;
oom_adj = lowmem_adj[array_size - 1];
if (oom_adj > OOM_ADJUST_MAX)
return;
oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
if (oom_score_adj <= OOM_ADJUST_MAX)
return;
lowmem_print(1, "lowmem_shrink:convert oom_adj to oom_score_adj:\n");
for (i = 0; i < array_size; i++) {
oom_adj = lowmem_adj[i];
oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);
lowmem_adj[i] = oom_score_adj;
lowmem_print(1, "oom_adj %d=> oom_score_adj %d\n",
oom_adj, oom_score_adj);
}
}
static intlowmem_adj_array_set(const char *val, const struct kernel_param *kp)
{
int ret;
ret = param_array_ops.set(val, kp);
/* HACK: Autodetect oom_adj values inlowmem_adj array */
lowmem_autodetect_oom_adj_values();
return ret;
}
static intlowmem_adj_array_get(char *buffer, const struct kernel_param *kp)
{
return param_array_ops.get(buffer, kp);
}
static voidlowmem_adj_array_free(void *arg)
{
param_array_ops.free(arg);
}
static structkernel_param_ops lowmem_adj_array_ops = {
.set = lowmem_adj_array_set,
.get = lowmem_adj_array_get,
.free = lowmem_adj_array_free,
};
static conststruct kparam_array __param_arr_adj = {
.max = ARRAY_SIZE(lowmem_adj),
.num = &lowmem_adj_size,
.ops = ¶m_ops_short,
.elemsize = sizeof(lowmem_adj[0]),
.elem = lowmem_adj,
};
#endif
module_param_named(cost, lowmem_shrinker.seeks, int,S_IRUGO | S_IWUSR);
#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
__module_param_call(MODULE_PARAM_PREFIX,adj,
&lowmem_adj_array_ops,
.arr = &__param_arr_adj,
S_IRUGO | S_IWUSR, -1);
__MODULE_PARM_TYPE(adj,"array of short");
#else
module_param_array_named(adj, lowmem_adj,short, &lowmem_adj_size,
S_IRUGO | S_IWUSR);
#endif
module_param_array_named(minfree, lowmem_minfree,uint, &lowmem_minfree_size,
S_IRUGO | S_IWUSR);
module_param_named(debug_level,lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
static intdebug_adj_set(const char *val, const struct kernel_param *kp)
{
const int ret = param_set_uint(val, kp);
lowmem_debug_adj =lowmem_oom_adj_to_oom_score_adj(lowmem_debug_adj);
return ret;
}
static structkernel_param_ops debug_adj_ops = {
.set = &debug_adj_set,
.get = ¶m_get_uint,
};
module_param_cb(debug_adj,&debug_adj_ops, &lowmem_debug_adj, S_IRUGO | S_IWUSR);
__MODULE_PARM_TYPE(debug_adj,short);
#else
module_param_named(debug_adj,lowmem_debug_adj, short, S_IRUGO | S_IWUSR);
#endif
module_param_named(candidate_log,enable_candidate_log, uint, S_IRUGO | S_IWUSR);
正常情况下,lowmemorykiller在此处总共导出了4个内核变量,其中两个是内核数组,即adj和mimfree。这两个就是我们需要关注的。我们可以在手机系统上如下位置访问到这些内核变量:/sys/module/lowmemorykiller/parameters。
在init.rc文件中会设置这些内核变量的属性(/androidLSoul45/system/core/rootdir/init.rc),以便于后面系统对这些值进行修改。
on early-init
# Set init and its forked children'soom_adj.
write/proc/1/oom_adj -16
。。。。。。。
# Memory management. Basic kernel parameters, and allow the high
# level system server to be able to adjustthe kernel OOM driver
# parameters to match how it is managingthings.
write /proc/sys/vm/overcommit_memory 1
write /proc/sys/vm/min_free_order_shift 4
chown root system/sys/module/lowmemorykiller/parameters/adj
chmod 0220/sys/module/lowmemorykiller/parameters/adj
chown root system/sys/module/lowmemorykiller/parameters/minfree
chmod 0220/sys/module/lowmemorykiller/parameters/minfree
。。。。。。。。
将init进程oom_adj设置为-16,从而保证init进程永远不会被杀掉
那通常情况下, adj值又是在什么时候设置的呢?设置这些值的依据是什么,是一个经验值呢还是有什么算法来算出那些值?
写到这里,想起了以前写的《SettingMemmory分类》里面有提到过dumpsysmeminfo –oom进程的映射问题。里面简单提到了进程是在ProcessList.java中根据不同的adj变量来处理,从而最终映射出来的。通过全局搜索整个androidL源码,也发现lowmemorykiller所导出的那些值只在ProcessList.updateOomLevels()方法中被修改。代码位置:
/local/sourcecode/AndroidLSoul45/frameworks/base/services/core/java/com/android/server/am/ProcessList.java现在我们来看看:
final classProcessList {
//*****进程分类*************//
// The minimum time we allow betweencrashes, for us to consider this
// application to be bad and stop and itsservices and reject broadcasts.
static final int MIN_CRASH_INTERVAL =60*1000;
// OOM adjustments for processes in variousstates:
// Adjustment used in certain places wherewe don't know it yet.
// (Generally this is something that isgoing to be cached, but we
// don't know the exact value in the cachedrange to assign yet.)
static final int UNKNOWN_ADJ = 16;
// This is a process only hostingactivities that are not visible,
// so it can be killed without anydisruption.
static final int CACHED_APP_MAX_ADJ = 15;
static final int CACHED_APP_MIN_ADJ = 9;
// The B list of SERVICE_ADJ -- these arethe old and decrepit
// services that aren't as shiny andinteresting as the ones in the A list.
static final int SERVICE_B_ADJ = 8;
// This is the process of the previousapplication that the user was in.
// This process is kept above other things,because it is very common to
// switch back to the previous app. This is important both for recent
// task switch (toggling between the twotop recent apps) as well as normal
// UI flow such as clicking on a URI in thee-mail app to view in the browser,
// and then pressing back to return toe-mail.
static final int PREVIOUS_APP_ADJ = 7;
// This is a process holding the homeapplication -- we want to try
// avoiding killing it, even if it wouldnormally be in the background,
// because the user interacts with it somuch.
static final int HOME_APP_ADJ = 6;
// This is a process holding an applicationservice -- killing it will not
// have much of an impact as far as theuser is concerned.
static final int SERVICE_ADJ = 5;
// This is a process with a heavy-weightapplication. It is in the
// background, but we want to try to avoidkilling it. Value set in
// system/rootdir/init.rc on startup.
static final int HEAVY_WEIGHT_APP_ADJ = 4;
// This is a process currently hosting abackup operation. Killing it
// is not entirely fatal but is generally abad idea.
static final int BACKUP_APP_ADJ = 3;
// This is a process only hostingcomponents that are perceptible to the
// user, and we really want to avoidkilling them, but they are not
// immediately visible. An example isbackground music playback.
static final int PERCEPTIBLE_APP_ADJ = 2;
// This is a process only hostingactivities that are visible to the
// user, so we'd prefer they don'tdisappear.
static final int VISIBLE_APP_ADJ = 1;
// This is the process running the currentforeground app. We'd really
// rather not kill it!
static final int FOREGROUND_APP_ADJ = 0;
// This is a system persistent process,such as telephony. Definitely
// don't want to kill it, but doing so isnot completely fatal.
static final int PERSISTENT_PROC_ADJ = -12;
// The system process runs at the defaultadjustment.
static final int SYSTEM_ADJ = -16;
// Special code for native processes thatare not being managed by the system (so
// don't have an oom adj assigned by thesystem).
static final intNATIVE_ADJ = -17;
//***上面也是android的进程分类。可以从帮助文档可以看出来****//
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
// The minimum number of cached apps wewant to be able to keep around,
// without empty apps being able to pushthem out of memory.
static final int MIN_CACHED_APPS = 2;
//The maximum number of cached processes we will keep around before killing them.
// NOTE: this constant is *only* a controlto not let us go too crazy with
// keeping around processes on devices withlarge amounts of RAM. For devices that
// are tighter on RAM, the out of memorykiller is responsible for killing background
// processes as RAM is needed, and weshould *never* be relying on this limit to
// kill them. Also note that this limit only applies tocached background processes;
// we have no limit on the number ofservice, visible, foreground, or other such
// processes and the number of thoseprocesses does not count against the cached
// process limit.
static final int MAX_CACHED_APPS = 32;
// We allow empty processes to stick aroundfor at most 30 minutes.
static final long MAX_EMPTY_TIME =30*60*1000;
// The maximum number of empty appprocesses we will let sit around.
private static final int MAX_EMPTY_APPS =computeEmptyProcessLimit(MAX_CACHED_APPS);
// The number of empty apps at which wedon't consider it necessary to do
// memory trimming.
static final int TRIM_EMPTY_APPS =MAX_EMPTY_APPS/2;
// The number of cached at which we don'tconsider it necessary to do
// memory trimming.
static final int TRIM_CACHED_APPS =(MAX_CACHED_APPS-MAX_EMPTY_APPS)/3;
// Threshold of number of cached+emptywhere we consider memory critical.
static final int TRIM_CRITICAL_THRESHOLD =3;
// Threshold of number of cached+emptywhere we consider memory critical.
static final int TRIM_LOW_THRESHOLD = 5;
// Low Memory Killer Daemon command codes.
// These must be kept in sync with thedefinitions in lmkd.c
//
// LMK_TARGET <minfree> <minkillprio>... (up to 6 pairs)
// LMK_PROCPRIO <pid> <prio>
// LMK_PROCREMOVE <pid>
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
// These are the various interesting memorylevels that we will give to
// the OOM killer. Note that the OOM killer only supports 6slots, so we
// can't give it a different value forevery possible kind of process.
private int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ,VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ,CACHED_APP_MAX_ADJ
};
// These are the low-end OOM levellimits. This is appropriate for an
// HVGA or smaller phone with less than512MB. Values are in KB.
private final int[] mOomMinFreeLow = newint[] {
8192, 12288, 16384,
24576, 28672, 32768
};
// These are the high-end OOM levellimits. This is appropriate for a
// 1280x800 or larger screen with around1GB RAM. Values are in KB.
private final int[] mOomMinFreeHigh = newint[] {
49152, 61440, 73728,
86016, 98304, 122880
};
// The actual OOM killer memory levels weare using.
private int[] mOomMinFree = newint[mOomAdj.length];
private final long mTotalMemMb;
private long mCachedRestoreLevel;
private boolean mHaveDisplaySize;
private static LocalSocket sLmkdSocket;
private static OutputStream sLmkdOutputStream;
ProcessList() {
MemInfoReader minfo = newMemInfoReader();
minfo.readMemInfo();
mTotalMemMb =minfo.getTotalSize()/(1024*1024);
updateOomLevels(0, 0, false);
}
void applyDisplaySize(WindowManagerServicewm) {
if (!mHaveDisplaySize) {
Point p = new Point();
wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
if (p.x != 0 && p.y != 0) {
updateOomLevels(p.x, p.y,true);
mHaveDisplaySize = true;
}
}
}
private void updateOomLevels(int displayWidth, int displayHeight, boolean write){
/// M: LCA: minfree only depends onmemory size @ {
if (SystemProperties.get("ro.mtk_lca_ram_optimize").equals("1")){
// in LCA, do not update the lmkminfree value, read the value to java part
String adj =readFile("/sys/module/lowmemorykiller/parameters/adj");
String minfree =readFile("/sys/module/lowmemorykiller/parameters/minfree");
String[] adjs =adj.split(",");
String[] minfrees =minfree.split(",");
try {
mOomAdj = new int[adjs.length];
mOomMinFree = newint[adjs.length];
for (int i = 0; i <adjs.length; ++i) {
mOomAdj[i] =Integer.parseInt(adjs[i]);
Log.i(ActivityManagerService.TAG, "adj: " + mOomAdj[i]);
}
for (int i = 0; i <adjs.length; ++i) {
mOomMinFree[i] =Integer.parseInt(minfrees[i]) * PAGE_SIZE / 1024;
Log.i(ActivityManagerService.TAG, "minfree: " +mOomMinFree[i]);
}
} catch (Exception e) {
Slog.w(ActivityManagerService.TAG,"parse lmk adj,minfree failed");
}
return;
}
/// @}
// Scale buckets from avail memory: at300MB we use the lowest values to
// 700MB or more for the top values.
//计算scale
float scaleMem =((float)(mTotalMemMb-300))/(700-300);
// Scale buckets fromscreen size.
int minSize =480*800; // 384000
int maxSize =1280*800; // 1024000 230400 870400 .264
float scaleDisp =((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
if (false) {
Slog.i("XXXXXX", "scaleMem=" + scaleMem);
Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + "dw=" + displayWidth
+ "dh=" + displayHeight);
}
float scale = scaleMem> scaleDisp ? scaleMem : scaleDisp;
if (scale < 0) scale = 0;
else if (scale > 1) scale = 1;
int minfree_adj =Resources.getSystem().getInteger(
com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
int minfree_abs =Resources.getSystem().getInteger(
com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
if (false) {
Slog.i("XXXXXX","minfree_adj=" + minfree_adj + " minfree_abs=" +minfree_abs);
}
final boolean is64bit =Build.SUPPORTED_64_BIT_ABIS.length > 0;
//第一轮计算各个adj所对应的minimumfree memory阈值。计算各个值的算式为(long)(low+ ((high-low)*scale))。low值和high值都是预定义的固定的经验值,比较关键的是那个scale值。在前面计算scale的部分,我们可以看到,它会先计算一个memory的scale值(为((float)(mTotalMemMb-300))/(700-300)),再计算一个屏幕分辨率的scale值
(((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize)),
首先取scale值为这两个scale值中较大的那一个。再然后是一些容错处理,将scale值限制在0~1之间,以防止设备内存小于300MB,同时设备分辨率小于480*800;或者,设备内存大于700MB,或设备分辨率大于1280*800的情况出现时,出现太不合理的阈值。
for (int i=0; i<mOomAdj.length; i++){
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
mOomMinFree[i] = (int)(low +((high-low)*scale));
if (is64bit) {
// On 64 bit devices, weconsume more baseline RAM, because 64 bit is cool!
// To avoid being all pagey andstuff, scale up the memory levels to
// give us some breathing room.
mOomMinFree[i] =(3*mOomMinFree[i])/2;
}
}
//第二轮计算各个adj所对应的minifree memory阈值。计算各个值的算式为(long)((float)minfree_abs* mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1])。此处给了各设备对lowmemory阈值进行定制的机会。各个设备可以在framework的config文件中定义config_lowMemoryKillerMinFreeKbytesAbsolute,以指定最大的adj所对应的freememory阈值,其他各个adj所对应的freememory阈值将依比例算出。config.xml在: AndroidLSoul45/frameworks/base/core/res/res/values/config.xml。
if (minfree_abs >= 0) {
for (int i=0; i<mOomAdj.length;i++) {
mOomMinFree[i] =(int)((float)minfree_abs * mOomMinFree[i]
/mOomMinFree[mOomAdj.length - 1]);
}
}
//第三轮计算各个adj所对应的minifree memory阈值。计算各个值的算式为mOomMinFree[i]+= (long)((float)minfree_adj * mOomMinFree[i] / mOomMinFree[mOomAdj.length -1])。此处是给特定设备微调lowmemory的阈值提供机会。不过我们仔细来看第二轮和第三轮的计算,这两轮计算似乎是可以合并为:(long)((float)(minfree_abs+ minfree_adj) * mOomMinFree[i] / mOomMinFree[mOomAdj.length- 1])。
if (minfree_adj != 0) {
for (int i=0; i<mOomAdj.length;i++) {
mOomMinFree[i] +=(int)((float)minfree_adj * mOomMinFree[i]
/mOomMinFree[mOomAdj.length - 1]);
if (mOomMinFree[i] < 0) {
mOomMinFree[i] = 0;
}
}
}
// The maximum size we will restore aprocess from cached to background, when under
// memory duress, is 1/3 the size wehave reserved for kernel caches and other overhead
// before killing background processes.
mCachedRestoreLevel =(getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
// Ask the kernel to try to keep enoughmemory free to allocate 3 full
// screen 32bpp buffers withoutentering direct reclaim.
int reserve = displayWidth *displayHeight * 4 * 3 / 1024;
int reserve_adj =Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
int reserve_abs =Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
if (reserve_abs >= 0) {
reserve = reserve_abs;
}
if (reserve_adj != 0) {
reserve += reserve_adj;
if (reserve < 0) {
reserve = 0;
}
}
//由前面构造适合设置给lowmemorykiller导出参数的字符串,并最终将构造的这组参数设置进去。
if (write) {
ByteBuffer buf =ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
for (int i=0; i<mOomAdj.length;i++) {
buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
writeLmkd(buf);
SystemProperties.set("sys.sysctl.extra_free_kbytes",Integer.toString(reserve));
}
// GB: 2048,3072,4096,6144,7168,8192
// HC:8192,10240,12288,14336,16384,20480
}
adj值是一组写死的固定的值,具体可以参考mOomAdj的定义。最后面如果是android4.4的话,则为:
//Slog.i("XXXXXXX","******************************* MINFREE: " + memString);
if (write) {
writeFile("/sys/module/lowmemorykiller/parameters/adj",adjString.toString());
writeFile("/sys/module/lowmemorykiller/parameters/minfree",memString.toString());
SystemProperties.set("sys.sysctl.extra_free_kbytes",Integer.toString(reserve));
}
这里还有一个问题,updateOomLevels()在那里使用?首先在前面的applyDisplaySize()方法中使用。在这个方法中,当能够从WMS中获取有效的屏幕分辨率时,会去更新oomlevels,并且更新之后就不会再次去更新。
通过搜索发现,只有在ActivityManagerService中的updateConfiguration()方法中调用了。具体看看:
public voidupdateConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
synchronized(this) {
if (values == null &&mWindowManager != null) {
// sentinel: fetch the currentconfiguration from the window manager
values =mWindowManager.computeNewConfiguration();
}
if (mWindowManager !=null) {
mProcessList.applyDisplaySize(mWindowManager);
}
final long origId =Binder.clearCallingIdentity();
if (values != null) {
Settings.System.clearConfiguration(values);
}
updateConfigurationLocked(values,null, false, false);
Binder.restoreCallingIdentity(origId);
}
}
这里总结一下OOMlevels的更新:更新是在系统启动后,第一个configurationchange的消息到AMS的时候。adj值为一组固定的预定义的值;各个adj所对应的minfree阈值则根据系统的内存大小和屏幕的分辨率计算得出,各个设备还可以通过framework的configconfig_lowMemoryKillerMinFreeKbytesAdjust
和config_lowMemoryKillerMinFreeKbytesAbsolute来定制最终各个adj所对应的minfree阈值。
这里还要提一下Process是怎么set adj值的。
通过Process.setOomAdj(intpid, int amt)进行设置,其中在ActivityManagerService中updateOomAdjLocked()函数中就有调用。它由android_util_Process.cpp实现该函数。
Process.java:
public static final native boolean setOomAdj(int pid,int amt);
android_util_Process.cpp:
jboolean android_os_Process_setOomAdj(JNIEnv* env,jobject clazz,
jint pid,jint adj)
{
#ifdef HAVE_OOM_ADJ
chartext[64];
sprintf(text, "/proc/%d/oom_adj",pid);
int fd =open(text, O_WRONLY);
if (fd >=0) {
sprintf(text, "%d", adj);
write(fd, text, strlen(text));
close(fd);
}
return true;
#endif
returnfalse;
}
1.3 low memory killer的触发
1.3.1 adj变化
我们先从手机上打印出adj设置值信息:
Pixi335:
user@swd2:/local/sourcecode/$ adb shell cat /sys/module/lowmemorykiller/parameters/adj
0,58,117,235,352,470,529,705,882
user@swd2:/local/sourcecode/$ adb shell cat /sys/module/lowmemorykiller/parameters/minfree
1024,3072,5120,6144,7168,8192,9216,10240,12288
如上文得知,lowmemorykiller.c导出了一些内核参数,以便于用户空间的程序可以对这些值进行重新配置。实际上,在system_server的AMS里,系统启动之后会根据系统所运行的具体状况,如屏幕分辨率,内存大小等,重新计算这些值,并设置给内核。结论也就是:lowmemorykillerdriver代码里面定义的那些值会被AMS给更改掉。
可以看lowmemorykiller.c里面lowmem_adj_array_set()这个函数,这个函数应该是在system_server设置adj值的时候会被调用到。这个方法里面调用了lowmem_autodetect_oom_adj_values(),在lowmem_autodetect_oom_adj_values()这个函数里面会再对oom_adj值做一些调整。那最后也就是说:由于内核导出了一些参数方便我们设置,所以在/sys/module/lowmemorykiller/parameters/adj里面设置的参数(0、1、2、4、6、8、9、12、15)会在系统启动时设置给内核,由于此时内核中对adj对应的数组lowmem_adj进行了重新赋值(计算之后的oom_score_adj),计算之后的数值也会同步给/sys/module/lowmemorykiller/parameters/adj这个文件。这个是module_param_array_named()这个函数的功能?
网上查了一下:sys 下的东西就是内核的东西。/sys/module/lowmemorykiller/parameters/adj这个里面的值,是在kerneldriver被加载进系统时,会是kernel里面预定义的那组值。在system_server设置那个值,也就是写sys文件的时候,给的值是类似于(0、1、2、4、6、8、9、12、15)这样的一组值。在写sys文件时,内核里面会有一些callback被调用到,就是lowmem_adj_array_set()。在这个函数里,会对system_server设置的(0、1、2、4、6、8、9、12、15)这样的一组值再做一个处理,就是最后看到的那组值。
1.3.2 LWK触发
low memory killer究竟是怎么执行起来的呢?不难猜测lowmemory killer的执行应该是在系统内存不足的时候。那lowmemory killer会在什么样的进程中执行呢?我们来构造一个系统memory 不足的场景:创建几个简单app,在每个app中都分配尽可能多的内存(分配内存过多进程会发生OutOfMemoryError并退出),如下面这样:
public classMainActivity extends Activity {
int[] data;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new int[20 * 1024 *1024];
for (int i = 0; i < data.length; ++i) {
data[i] = i;
}
}
每一个app(如:out1)会分配80M的内存,让类似这样的几个app在设备上执行起来。然后我们将可以看到内核吐出来的log:
out1、out2、out3、out4都被kill掉,lowmemorykiller运行起来咯,也就是lowmemorykiller.c这段代码运行吐出来的。
if (selected) {
lowmem_print(1, "Killing '%s' (%d), adj %d, score_adj%hd,\n" \
" to free %ldkB on behalf of '%s' (%d)because\n" \
" cache %ldkB is below limit %ldkB foroom_score_adj %hd\n" \
" Free memory is %ldkB above reserved\n",
selected->comm, selected->pid,
REVERT_ADJ(selected_oom_score_adj),
selected_oom_score_adj,
selected_tasksize * (long)(PAGE_SIZE /1024),
current->comm, current->pid,
other_file * (long)(PAGE_SIZE / 1024),
minfree * (long)(PAGE_SIZE / 1024),
min_score_adj,
other_free * (long)(PAGE_SIZE / 1024));
lowmem_deathpending_timeout =jiffies + HZ;
仔细观察log截图,发现执行了lowmemorykiller的进程总共有3个:内核线程kswapd0、native app adbd和app com.example.outX。由此,我们推测lowmemorykiller大概有两种执行方式:
(1)是在kswapd0中,kswapd内核线程会被定期唤醒,当它发现系统内存不足的时候,它就会去执行lowmemorykiller杀进程,并回收内存。
(2)是在app中,当一个app请求分配内存,而系统又没有足够的内存可供分配时,则分配内存的请求将会先被阻塞,执行lowmemorykiller杀进程,回收内存之后再尝试着去分配内存。