Android平台App进程优先级

我们都知道Android手机上是可以安装很多App的,每一个App至少是会有一个进程的。创建进程是件麻烦而且耗资源的事情,Android为了让App启动的时候能更快,会把那么暂时不使用的App的进程缓存起来,但是内存是有限的啊,总不能让所有的进程都放在内存里边吧,所以Android有一个淘汰机制,会根据App的运行状态设置一个进程的优先级(oom_adj),然后根据内存的紧张程度,把那些优先级低(oom_adj值大)的进程kill掉,以保证其他的进程能够有足够的内存使用。

进程

  1. Zygote进程
    这个是Android框架的主要进程,所有的App进程以及系统服务进程SystemServer都是由Zygote进程Fork出来的
  2. App的主进程
    每一个App的运行都是在一个独立的进程,进程的名字就是App的packagename,这些进程都是从Zygote进程Fork出来的,并受AMS(ActivityManagerService)管理
  3. App的辅助进程
    可以允许App有多个进程,在AndroidManifest.xml里边配置android:process属性,就可以开启多进程,这些进程名字都是packagename:name这种,以区分是属于哪个App,我一般称之为辅助进程。但这些进程也都跟主进程一样,也是从Zygote进程Fork出来的,并受AMS管理
  4. Native进程
    Android除了使用Java,还有NDK,可以使用C/C++去开发,然后这里也是可以Fork出进程的,我一般称之为Native进程,Native进程可以不受AMS管理,自由度很大,本文暂且不讲

优先级

App进程的优先级是在com.android.server.am.ProcessList类里边定义的,这个类在Android的API里边是找不到的,要想看看里边的实现可以去Android SDK里边去找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java

主要有这么几个优先级(oom_adj值):

  • UNKNOWN_ADJ = 16
    预留的最低级别,一般对于缓存的进程才有可能设置成这个级别

Adjustment used in certain places where we don't know it yet. (Generally this is something that is going to be cached, but we don't know the exact value in the cached range to assign yet.)

  • CACHED_APP_MAX_ADJ = 15
    缓存进程,空进程,在内存不足的情况下就会优先被kill

This is a process only hosting activities that are not visible, so it can be killed without any disruption.

  • CACHED_APP_MIN_ADJ = 9
    缓存进程,也就是空进程
  • SERVICE_B_ADJ = 8
    不活跃的进程

The B list of SERVICE_ADJ -- these are the old and decrepit services that aren't as shiny and interesting as the ones in the A list.

  • PREVIOUS_APP_ADJ = 7
    切换进程

This is the process of the previous application 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 two top recent apps) as well as normal UI flow such as clicking on a URI in the e-mail app to view in the browser, and then pressing back to return to e-mail.

  • HOME_APP_ADJ = 6
    与Home交互的进程

This is a process holding the home application -- we want to try avoiding killing it, even if it would normally be in the background, because the user interacts with it so much.

  • SERVICE_ADJ = 5
    有Service的进程

This is a process holding an application service -- killing it will not have much of an impact as far as the user is concerned.

  • HEAVY_WEIGHT_APP_ADJ = 4
    高权重进程

This is a process with a heavy-weight application. It is in the background, but we want to try to avoid killing it. Value set in system/rootdir/init.rc on startup.

  • BACKUP_APP_ADJ = 3
    正在备份的进程

This is a process currently hosting a backup operation. Killing it is not entirely fatal but is generally a bad idea.

  • PERCEPTIBLE_APP_ADJ = 2
    可感知的进程,比如那种播放音乐

This is a process only hosting components that are perceptible to the user, and we really want to avoid killing them, but they are not immediately visible. An example is background music playback.

  • VISIBLE_APP_ADJ = 1
    可见进程

This is a process only hosting activities that are visible to the user, so we'd prefer they don't disappear.

  • FOREGROUND_APP_ADJ = 0
    前台进程

This is the process running the current foreground app. We'd really rather not kill it!

  • PERSISTENT_SERVICE_ADJ = -11
    重要进程

This is a process that the system or a persistent process has bound to, and indicated it is important.

  • PERSISTENT_PROC_ADJ = -12
    核心进程

This is a system persistent process, such as telephony. Definitely don't want to kill it, but doing so is not completely fatal.

  • SYSTEM_ADJ = -16
    系统进程

The system process runs at the default adjustment.

  • NATIVE_ADJ = -17
    系统起的Native进程

Special code for native processes that are not being managed by the system (so don't have an oom adj assigned by the system).

在Android-18及以下空进程不叫CACHED_APP_MIN_ADJ ,叫HIDDEN_APP_MIN_ADJ,有这么点不一样,但是值都一样。

如何查看App进程优先级

对于App进程,因为受AMS管理,都会有一个oom_adj的文件记录着oom_adj的值,但是对于Native进程,那AMS就管不着了。

//oom_adj的值就是进程的优先级,
//查看oom_adj值
cat /proc/${pid}/oom_adj

lowmemorykiller 机制

这个机制是在goldfish层面实现的,如果要想具体了解是怎么选择杀进程的话,需要去看Android drivers下的lowmemorykiller.c的代码

编译过goldfish的人,应该知道它其实就是Linux kernel。因为Android是基于Linux的操作系统嘛,kernel其实也是跟着Linux kernel走的,只不过在里边需要开发(或者精简)一些专门针对Android的东西,lowmemorykiller 就是其中的一部分,是个驱动。至于为啥会被放在drivers/staging目录下,可以参考下这篇文章《小议Linux staging tree》

有这么几个关键点:

  1. 向系统注册lowmem_shrinker回调,当系统空闲内存不足时调用,去杀进程
  2. 把空闲内存低于多少,就去杀那个级别的进程,我称之为策略
  3. 杀的策略由application层根据内存状况指定,写入文件传递给驱动
  4. 根据策略会计算出min_score_adj
  5. 每个App进程都有oom_adj值,根据oom_adj值计算出oom_score_adj(得分值),oom_adj越大,oom_score_adj越高,高于min_score_adj的被列入死亡名单
  6. 在死亡名单里面选一个占内存最大的进程kill掉

所以当内存不足的时候,进程优先级低的(oom_adj越大的),占内存大的App进程将会被优先kill掉。

至于这个策略嘛,各个ROM可能不一致的,根据内存的大小,系统的严格程度来制定,但都只能分6个级别。


查看传给lowmemorykiller驱动的策略文件

minfree:列举6个内存阀值。
比如:32768,61440,73728,129024,147456,184320
adj:列举6个oom_adj的级别。
比如:0,1,2,3,9,15
这两个值相互对应,空闲内存低于多少的时候就kill那个级别的进程。

ProcessList.java里边定义的策略参数

当然有些ROM也会把这个参数定义在init.rc里边,开机后再写入minfree和adj文件,传递给驱动

trimApplications机制

仅仅只有低内存的情况下才去kill进程吗?很明显不是,对于App的运行是有很多状况的,比如Crash、App退出、系统清理、卸载。

trimApplications是一个方法,定义在ActivityManagerService里边,Android API里边也是没有这个类的,得去SDK里边找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ActivityManagerService.java

这个机制大概也有几个关键点:

  1. package已被卸载的无用进程会被Kill
  2. persistent的App会被优先照顾,进程优先级设置为PERSISTENT_PROC_ADJ=-12
  3. Activity仅在主进程跑的App会被认为是高权重进程,HEAVY_WEIGHT_APP_ADJ=4
  4. 只有前台进程才是FOREGROUND_APP_ADJ=0,前台进程不会被杀
  5. 每当Activity、Service等生命周期发生变化的时候都会引起进程的oo_adj的调整
  6. 进程里边没有任何的Activity的存在优先被杀
  7. 空进程最容易被杀

如何提高后台App进程的优先级

其实Android框架的思想是很好的,对于空的进程,没事干的进程直接kill掉,对于用户体验来讲是不会有影响的,但是往往我们的App都会有推送这个功能,恰巧GCM(Google Cloud Messaging)在国内又不能用,所以很多情况下我们也会希望App在后台的时候也尽量不要被杀。

可以考虑把后台App进程的优先级提高,下面有几个方法:

  • AndroidManifest.xml中配置persistent属性
<application android:name="App"
  android:persistent="true"
  android:label="@string/dialerIconLabel"
  android:icon="@drawable/ic_launcher_phone">
  • 重载back按键事件,让activity在后台运行,不要Destory
  • 开Service,并设置前台运行方式
  • 与NotificationManager交互,让进程变成可感知进程
  • 发送/接收广播,别让自己变成空进程

很明显这会消耗更多的电量,也有点流氓,有些需求也许本就不该做。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,504评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,898评论 1 300
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,218评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,322评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,693评论 3 290
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,812评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,010评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,747评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,476评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,700评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,190评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,541评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,206评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,129评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,903评论 0 199
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,894评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,748评论 2 274

推荐阅读更多精彩内容