Android App 保活之 ADJ 算法

Android开发中,令人头疼的保活问题始终缠绕每一个开发者。如何保证自己的进程不被系统回收呢?首当其冲应该是保证自己进程的优先级。

Android系统在运行时,如果遭遇到内存过低,为保证系统稳定与流畅,会回收一部分不常用的进程(当然很多三方rom如miui会在电量过低也会回收)。这个回收过程当然不是随意回收,系统需要有一个判断进程优先级的指标,帮助系统判断哪些资源是优先级高需要保留,哪些资源优先级比较低需要释放该资源。

ADJ就是系统杀死进程的重要指标

本文从linux\Android进程优先级:adj分数,浅析一下如何查看adj以及各个adj分数背后所代表的含义。

1.ADJ 如何查看

利用adb shell

1.ps | grep 包名  //查看当前app的进程号

2.cat /proc/进程号/oom_adj  //查看当前进程的adj值(早期android和linux使用,现已废弃,但仍然有效)

3.cat /proc/进程号/oom_score_adj    //这个是新版本的查看adj的命令,adj有效值为-1000~1000

2.ADJ的值的各种含义

ADJ级别                    取值 含义

NATIVE_ADJ              -1000   native进程
SYSTEM_ADJ              -900    仅指system_server进程
PERSISTENT_PROC_ADJ     -800    系统persistent进程
PERSISTENT_SERVICE_ADJ  -700    关联着系统或persistent进程
FOREGROUND_APP_ADJ      0       前台进程
VISIBLE_APP_ADJ         100     可见进程
PERCEPTIBLE_APP_ADJ     200     可感知进程,比如后台音乐播放
BACKUP_APP_ADJ          300     备份进程
HEAVY_WEIGHT_APP_ADJ    400     重量级进程
SERVICE_ADJ             500     服务进程(A list中的service)
HOME_APP_ADJ            600     Home进程
PREVIOUS_APP_ADJ        700     上一个进程
SERVICE_B_ADJ           800     B List中的Service
CACHED_APP_MIN_ADJ      900     不可见进程的adj最小值
CACHED_APP_MAX_ADJ      906     不可见进程的adj最大值

3.ADJ触发顺序

ADJ是一种算法,用于系统判断进程优先级以触发Linux的LMK(LowMemoryKill)机制。一般触发时机(Linux下)是在系统低内存时,为了维护正在运行的进程,杀掉优先级比较低(adj值比较高)的其他进程。

在Android中这一机制有所改动。在ActivityManagerService里有具体的计算Adj值的源码。

进程刚启动时ADJ等于INVALID_ADJ,当执行完attachApplication(),该该进程的curAdj和setAdj不相等,则会触发执行setOomAdj()将该进程的节点/proc/pid/oom_score_adj写入oomadj值。下图参数为Android原生阈值,当系统剩余空闲内存低于某阈值(比如147MB),则从ADJ大于或等于相应阈值(比如900)的进程中,选择ADJ值最大的进程,如果存在多个ADJ相同的进程,则选择内存最大的进程。 如下是64位机器,LMK默认阈值图:

----------ADJ----------------Memory Left------------
FOREGROUND_APP_ADJ(0)          73MB
VISIBLE_APP_ADJ(100)           92MB
PERCEPTIBLE_APP_ADJ(200)       110MB
BACKUP_APP_ADJ(300)            129MB
CACHED_APP_MIN_ADJ(900)        221MB
CACHED_APP_MAX_ADJ(906)        332MB

4.高级进程 ADJ<0的进程

1.NATIVE_ADJ(-1000):是由init进程fork出来的Native进程,并不受system管控;

2.SYSTEM_ADJ(-900):是指system_server进程;

3.PERSISTENT_PROC_ADJ(-800): 是指在AndroidManifest.xml中申明android:persistent=”true”的系统(即带有FLAG_SYSTEM标记)进程,persistent进程一般情况并不会被杀,即便被杀或者发生Crash系统会立即重新拉起该进程。

4.PERSISTENT_SERVICE_ADJ(-700):是由startIsolatedProcess()方式启动的进程,或者是由system_server或者persistent进程所绑定(并且带有BIND_ABOVE_CLIENT或者BIND_IMPORTANT)的服务进程

5.总结

Android进程优先级ADJ的每一个ADJ级别往往都有多种场景,使用adjType完美地区分相同ADJ下的不同场景; 不同ADJ进程所对应的schedGroup不同,从而分配的CPU资源也不同,schedGroup大体分为TOP(T)、前台(F)、后台(B); ADJ跟AMS中的procState有着紧密的联系。

1.adj:通过调整oom_score_adj来影响进程寿命(Lowmemorykiller杀进程策略);

2.schedGroup:影响进程的CPU资源调度与分配;

3.procState:从进程所包含的四大组件运行状态来评估进程状态,影响framework的内存控制策略。比如控制缓存进程和空进程个数上限依赖于procState,再比如控制APP执行handleLowMemory()的触发时机等。
为了说明整体关系,以ADJ为中心来讲解跟adjType,schedGroup,procState的对应关系,下面以一幅图来诠释整个ADJ算法的精髓,几乎涵盖了ADJ算法调整的绝大多数场景。

image

6.最后,开发时应注意:

1.UI进程与Service进程一定要分离,因为对于包含activity的service进程,一旦进入后台就成为”cch-started-ui-services”类型的cache进程(ADJ>=900),随时可能会被系统回收;而分离后的Service进程服务属于SERVICE_ADJ(500),被杀的可能性相对较小。尤其是系统允许自启动的服务进程必须做UI分离,避免消耗系统较大内存。
只有真正需要用户可感知的应用,才调用startForegroundService()方法来启动前台服务,此时ADJ=PERCEPTIBLE_APP_ADJ(200),常驻内存,并且会在通知栏常驻通知提醒用户,比如音乐播放,地图导航。切勿为了常驻而滥用前台服务,这会严重影响用户体验。

2.进程中的Service工作完成后,务必主动调用stopService或stopSelf来停止服务,避免占据内存,浪费系统资源;

3.不要长时间绑定其他进程的service或者provider,每次使用完成后应立刻释放,避免其他进程常驻于内存;

4.APP应该实现接口onTrimMemory()和onLowMemory(),根据TrimLevel适当地将非必须内存在回调方法中加以释放。当系统内存紧张时会回调该接口,减少系统卡顿与杀进程频次。

5.减少在保活上花心思,更应该在优化内存上下功夫,因为在相同ADJ级别的情况下,系统会选择优先杀内存占用的进程。

原文:https://blog.csdn.net/zhangbijun1230/article/details/81347749

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

推荐阅读更多精彩内容