前言
进程优先级实际上是系统对进程重要性的一个客观评价。根据这个评价的结果来为进程分配不同的系统资源,这个资源包括内存资源和CPU资源。为了保证“公平公正”的评价每个进程,Google工程师为此设计了一套评价系统。本文试图从面相对象的角度和功能设计者角度来理解该功能,具体思路如下:
要实现一个功能,有必要的时候我们会去为他制定一些属性,对于设计者而言这些属性肯定是必须的,因此作为使用者我们必须理解这些属性的意义是什么。
同样要实现一个功能,设计者会定制一些该功能特有的行为,在代码里面体现为特定的函数,那么我们作为使用者,必须了解以下几个面:a.该行为的能产生什么样的结果,即行为的目的,b.该行为的触发条件,即什么时候会用到这些方法,为什么在这个时候要去触发?
本文会从如下三个关键词和三个关键方法来展开分析和讨论
- adj:通过调整oom_score_adj来影响进程寿命(Lowmemorykiller杀进程策略);
- schedGroup:影响进程的CPU资源调度与分配;
- procState:从进程所包含的四大组件运行状态来评估进程状态,影响framework的内存控制策略。比如控制缓存进程和空进程个数上限依赖于procState,再比如控制APP执行handleLowMemory()的触发时机等
- updateOomAdjLocked:更新
- computeOomAdjLocked:计算
- applyOomAdjLocked:应用
ADJ
AMS中有几个和adj相关的非常重要的方法:
- updateOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord TOP_APP, boolean doingAll, long now)
- updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll)
- updateOomAdjLocked()
- computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now)
- applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
long nowElapsed)
这个几个方法的关系如下:updateOomAdjLocked(更新),包括computeOomAdjLocked(计算)和applyOomAdjLocked(应用),这俩个动作!
总体算法示意图
updateOomAdjLocked触发场景
该方法作为adj更新的核心方法,设计者用它来更新进程的adj。我们在前言说过进程优先级的评价是“公平公正”的,但是“公平公正”并不等同于平均,平均分配系统资源实际上是不符合用户体验的需求的。用户必定希望自己想用的进程能够得到足够的系统资源。没有使用的进程尽可能的分配较少的资源。因此,设计者必须了解当前的进程所处的场景。然后,基于不同场景来及时的更新adj,及时的重新分配系统资源。因此,设计必须在Android系统中进行“埋点”。
下面是搜索的updateOomAdjLocked,该方法的埋点结果。涉及的类
updateOomAdjLocked大致可以分为俩类:1.带参数的(在后面把它称为updateOomAdjLockedA:针对指定的单个进程更新优先级,如果oomAdjAll这个参数为true,就有可能去调用updateOomAdjLockedB),2.带参数的(在后面把他成为updateOomAdjLockedB对所有进程更新优先级)。
1.ActiveServices
- bindServiceLocked:最开始是由于AMS中的binderervices被调用,即该场景可以理解为当有服务绑定的时候,就会有updateOomAdjLockedB,当然由于绑定服务只单个应用的行为,那么updateOomAdjLockeB也是针对单个应用的。之所有绑定服务的时候,需要调用updateOomAdjLockedB,是因为,服务进程的adj是由,client和server端之间的关系来决定的。
- unbindServiceLocked: 最开始是由于AMS中的unbindService被调用,即该场景可以理解为当有服务解绑的时候,就会有updateOomAdjLockedB。这个原因和第一点是一样的,服务的解绑也会导致client端和server之间的关系发生变化。
- unbindServiceLocked:这里还进行了updateOomAdjLockedA
- realStartServiceLocked:这个最开始是由于AMS中startServices或者attachApplicationLocked被调用。即该场景可以理解为:有startServices或者新的进程起来,会去updateOomAdjLockedA.这原因可以理解为:一个服务或者进程起来,它会导致系统其它进程的状态发生变化,(比如一个进程的前后台关系),因此要调用就会有updateOomAdjLockedA,
- sendServiceArgsLocked:
即服务起来或者进程别杀的时候。这个函数是一个私有函数,被调用的地方如下:
第三个参数为:adj值是否已经调整过,调整或就不在进行更新,
即有三处调用:
bringUpServiceLocked:即服务起来的时候。
realStartServiceLocked :第三个参数为false,因此,不用更新。第二不用更新的原因是,第二方法最终是在realStartServiceLocked被调用的,在该方法调用之前,前面已经调用过updateOomAdjLockedA。
cleanUpRemovedTaskLocked:进程被杀死的时候调用
- bringDownServiceLocked:服务解绑的场景相关,服务解绑
- bringDownServiceLocked:服务解绑的场景相关,服务destroy
- removeConnectionLocked:最开始是由于服务被解绑或者服务被杀死。调用updateOomAdjLockedB
- serviceDoneExecutingLocked
BroadcastQueue
1.processCurBroadcastLocked:在发送广播(sendPendingBroadcastsLocked
)和处理广播(processNextBroadcast)的时候调用updateOomAdjLockedA
2. deliverToRegisteredReceiverLocked:处理有序广播的时候,调用updateOomAdjLockedB,由于是挨个处理,因此调用针对单个应用的。
3. processNextBroadcastLocked:在处理完最后一个有序广播后,调用updateOomAdjLockedA,需要重新更新所以进程的adj
3.ActivityManagerService
1.onWakefulnessChanged:在充电过程中需要更新adj,调用updateOomAdjLockedA
2.setSystemProcess:在systerm_server进程起来的时候调用updateOomAdjLockedA更新全部应用的adj
3.AMS构造函数中:updateOomAdjLockedA
4.startActivityAsUserEmpty:空进程创建的时候调用updateOomAdjLockedB
5.appDiedLocked:进程死掉的时候,调用updateOomAdjLockedA
6.killAllBackgroundProcesses:用shell命令把进程杀死的时候会调用updateOomAdjLockedA
7.killPackageProcessesLocked:通过包名杀进程的时候updateOomAdjLockedA
8.attachApplicationLocked:进程创建的时候updateOomAdjLockedA
9.importanceTokenDied:updateOomAdjLockedA
4.ActivityStack
1. resumeTopActivityInnerLocked:Activity所在的栈和activity可见的时候
2. finishSubActivityLocked:最开始是由于Activity被finish,被finishd 时候会调用updateOomAdjLockedA
3. finishVoiceTask:activities are using voice被finish的时候被调用
4. finishCurrentActivityLocked:updateOomAdjLockedA
5. destroyActivityLocked:Activity被销毁的调用updateOomAdjLockedA
5.ActivityStackSupervisor
- realStartActivityLocked:新启动一个actvity
updateOomAdjLocked 逻辑分析
该函数的算法过程示意图:
1.初始化参数
2.为各类进程分配adj
3.计算各个类型进程的数量,杀进程
- 计算和设置内存level
ProcState |
trimMemoryLevel |
PROCESS_STATE_PERSISTENT = 0; |
TRIM_MEMORY_RUNNING_CRITICAL = 5 |
PROCESS_STATE_PERSISTENT_UI = 1; |
|
PROCESS_STATE_TOP = 2; |
|
PROCESS_STATE_FOREGROUND_SERVICE = 3; |
|
PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4; |
|
PROCESS_STATE_IMPORTANT_FOREGROUND = 5; |
|
PROCESS_STATE_IMPORTANT_BACKGROUND = 6; |
TRIM_MEMORY_UI_HIDDEN =20 |
PROCESS_STATE_TRANSIENT_BACKGROUND = 7; |
|
PROCESS_STATE_BACKUP = 8; |
|
PROCESS_STATE_SERVICE = 9; |
|
PROCESS_STATE_RECEIVER = 10; |
|
PROCESS_STATE_TOP_SLEEPING = 11; |
|
PROCESS_STATE_HEAVY_WEIGHT = 12; |
TRIM_MEMORY_BACKGROUND = 40 |
PROCESS_STATE_HOME = 13; |
TRIM_MEMORY_COMPLETE = 80 |
PROCESS_STATE_LAST_ACTIVITY = 14; |
|
PROCESS_STATE_CACHED_ACTIVITY = 15; |
|
PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16; |
|
PROCESS_STATE_CACHED_RECENT = 17; |
|
PROCESS_STATE_CACHED_EMPTY = 18; |
|
PROCESS_STATE_NONEXISTENT = 19; |
TRIM_MEMORY_UI_HIDDEN 表示应用程序的 所有UI界面被隐藏了.这个进程本身还是比较重要的,不能被销毁的,但是由于目前内存很低了,因此必须释放一些不是非常重要的资源。
下面三个等级应用程序真正运行时的回调:
TRIM_MEMORY_RUNNING_MODERATE 表示这个进程本身还是比较重要的,不能被销毁的。但是目前手机的内存running moderately low on memory,
TRIM_MEMORY_RUNNING_LOW 表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经is running low on memory,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。
TRIM_MEMORY_RUNNING_CRITICAL 表示应用程序仍然正常运行,但是内存已经非常低了(is running extremely low)但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务。
当应用程序是缓存的,会有如下内存回收的类型:
TRIM_MEMORY_BACKGROUND 表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。
TRIM_MEMORY_MODERATE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。
TRIM_MEMORY_COMPLETE 表示手机目前内存已经很低了,并且进程处于LRU缓存列表的最边缘位置,系统如果还是没有找到更多的内存那么该进程就会被杀掉,在这个时候应当尽可能地把一切可以释放的东西都进行释放。
5.TrimMemory
进程的curProcState可能取值,以及对应的场景见ProcState这一节
UidChange和后台行为受限 (见另外一篇博客)
computeOomAdjLocked
该函数的算法过程示意图:
1.ADJ级别
ADJ可能取值如下表。但是有时候不限于下面的值,对于不可见进程系统只是规定了最小值
CACHED_APP_MIN_ADJ(900),最大值CACHED_APP_MAX_ADJ(906)。对于不可见进程来说,那么他取值会在这个范围之间[900,901,902,903,904,905,906]
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 |
服务进程 |
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最大值 |
2.前置条件和参数初始化处理
- 处理app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ这种高优先级情况
这种情况的结果为:
curSchedGroup |
ProcessList.SCHED_GROUP_DEFAULT |
ProcessList.SCHED_GROUP_TOP_APP |
|
ProcessList.SCHED_GROUP_RESTRICTED |
|
curProcState |
ActivityManager.PROCESS_STATE_PERSISTENT |
ActivityManager.PROCESS_STATE_PERSISTENT_UI |
|
ActivityManager.PROCESS_STATE_PERSISTENT_UI |
|
curAdj |
<= ProcessList.FOREGROUND_APP_ADJ |
- 定义FOREGROUND_APP_ADJ的具体场景
Case |
adj |
procState |
schedGroup |
PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP |
FOREGROUND_APP_ADJ |
PROCESS_STATE_TOP |
SCHED_GROUP_TOP_APP |
app.runningRemoteAnimation |
VISIBLE_APP_ADJ |
PROCESS_STATE_TOP_SLEEPING |
SCHED_GROUP_TOP_APP |
当instrumentation不为空时 |
FOREGROUND_APP_ADJ |
PROCESS_STATE_FOREGROUND_SERVICE |
SCHED_GROUP_DEFAULT |
当进程存在正在接收的broadcastrecevier |
FOREGROUND_APP_ADJ |
PROCESS_STATE_RECEIVER |
SCHED_GROUP_DEFAULT |
当进程存在正在执行的service |
FOREGROUND_APP_ADJ |
PROCESS_STATE_SERVICE |
SCHED_GROUP_DEFAULT |
app == TOP_APP |
FOREGROUND_APP_ADJ |
PROCESS_STATE_TOP_SLEEPING |
SCHED_GROUP_BACKGROUND |
以上条件都不符合 |
adj=cachedAdj |
PROCESS_STATE_CACHED_EMPTY |
SCHED_GROUP_BACKGROUND |
- 定义VISIBLE_APP_ADJ和PERCEPTIBLE_APP_ADJ具体场景
ProcessList.VISIBLE_APP_ADJ的场景定义如果没有前台的activity:但是activity是可见
PERCEPTIBLE_APP_ADJ的场景定义:ActivityState.PAUSING, ActivityState.PAUSED r.isState(ActivityState.STOPPING) 在这些状态系统认为这些应用优先级比较高,如果adj的值大于PERCEPTIBLE_APP_ADJ就把他调整到PERCEPTIBLE_APP_ADJ,还有前台服务他的adj值也会被调整到 PERCEPTIBLE_APP_ADJ
这俩种场景对应的schedGroup都是>= SCHED_GROUP_DEFAULT
- 定义HEAVY_WEIGHT_APP_ADJ的具体场景
adj |
<=ProcessList.HEAVY_WEIGHT_APP_ADJ |
schedGroup |
ProcessList.SCHED_GROUP_BACKGROUND |
procState |
<=ActivityManager.PROCESS_STATE_HEAVY_WEIGHT |
- 定义HOME_APP_AD的具体场景
adj |
<=ProcessList.HOME_APP_ADJ |
schedGroup |
ProcessList.SCHED_GROUP_BACKGROUND |
procState |
<=ActivityManager.PROCESS_STATE_HOME |
- 定义PREVIOUS_APP_ADJ的具体场景
adj |
<=ProcessList.PREVIOUS_APP_ADJ |
schedGroup |
ProcessList.SCHED_GROUP_BACKGROUND |
procState |
<=ActivityManager.PROCESS_STATE_LAST_ACTIVITY |
- 定义BACKUP_APP_ADJ的具体场景
adj |
<=ProcessList.BACKUP_APP_ADJ |
procState |
<=ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND |
- 根据client和service的关系来确定service的adj
这个关系主要是指服务的caller, 主要依据调用bindService(Intent service, ServiceConnection conn, int flags)时候传入的flags参数来判断,这个flags可以选择的值为:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORIT
12.根据client和provide的关系来确定service的adj
applyOomAdjLocked
该函数的算法过程示意图:
1.把计算出来的ADJ设下去
2.分配不同的ProcessGroup
3. 把计算出来的进程状态设下去
- 重新计算PSS
来源:CSDN
作者:Proton.Chen
链接:https://blog.csdn.net/qq819025485/article/details/103743858