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

你可能感兴趣的:(Android App 保活之 ADJ 算法)