android 内存管理之adj 《三》

2024-02-11 16:59
文章标签 android 内存 管理 adj

本文主要是介绍android 内存管理之adj 《三》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

1.1 oom_adj与oom_score_adj

Android系统的设计理念正是希望应用进程能尽量长时间地存活,以提升用户体验。因此当应用推入到后台的时候进程并不会被立即杀死,而是存活一段时间,这样下次再使用则会非常快。但是如果处于后台的进程过多导致内存不足,此时就必须有选择的杀死部分进程。

到底该回收哪个进程呢?那么需要一个能管理所有进程,根据一定策略来释放进程的策略,这便有了lmk,全称为LowMemoryKiller(低内存杀手),lmkd来决定什么时间杀掉什么进程。

系统根据进程的组件状态来决定每个进程的优先级值oom_adj,oom_adj 是 lmkd 内部定义的一个变量,其余oom_adj 存在一个映射关系,Android 7.0之前的版本,oom_score_adj= oom_adj * 1000/17; 而Android 7.0开始,oom_score_adj= oom_adj,不用再经过一次转换。

lmkd 主要是通过进程的oom_score_adj来判定进程的重要程度,其会按照oom_score_adj由大到小的顺序 杀死进程,依此类推,以回收预期的可用系统资源,从而保证系统正常运转。

1.2  ADJ 级别

ADJ级别取值含义
NATIVE_ADJ-1000native进程
SYSTEM_ADJ-900仅指system_server进程
PERSISTENT_PROC_ADJ-800系统persistent进程
PERSISTENT_SERVICE_ADJ-700关联着系统或persistent进程
FOREGROUND_APP_ADJ0前台进程
VISIBLE_APP_ADJ100可见进程
PERCEPTIBLE_APP_ADJ200可感知进程,比如后台音乐播放
BACKUP_APP_ADJ300备份进程
HEAVY_WEIGHT_APP_ADJ400重量级进程
SERVICE_ADJ500服务进程
HOME_APP_ADJ600Home进程
PREVIOUS_APP_ADJ700上一个进程
SERVICE_B_ADJ800B List中的Service
CACHED_APP_MIN_ADJ900不可见进程的adj最小值
CACHED_APP_MAX_ADJ906不可见进程的adj最大值

从Android 7.0开始,ADJ采用100、200、300;在这之前的版本ADJ采用数字1、2、3,这样的调整可以更进一步地细化进程的优先级,比如在VISIBLE_APP_ADJ(100)与PERCEPTIBLE_APP_ADJ(200)之间,可以有ADJ=101、102级别的进程。

A-Service与B-Service的划分

        所有启动了服务的进程,且该服务所在的进程没有显示过UI,且该服务未执行startForeground(执行后会变为perveptible服务)动作,那该进程则为A-Service与B-Service中的一种。然后根据这类服务进程所处于Lru进程表中的位置,前1/3点服务为A-Service,其余的则为B-Service。

perceptible的标准

        perceptible名为可感知的进程,但并不是说能够感知到进程就一定表示该进程属于perveptible进程,比如播放音乐的进程活着状态栏上有通知的进程,虽然能够感知到进程的存在,但是不代表进程一定时perceptible类别的进程。决定该进程是否属于perceptible进程并未进程的可感知性,而是该进程的服务是否执行了startForeground动作。 

1.3  computeOomAdjLocked

第一部分

 private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,boolean doingAll, long now) {
//        updateOomAdjLocked函数每次更新oom_adj时,都会分配一个序号//此处就是根据序号判断是否已经处理过命令if (mAdjSeq == app.adjSeq) {// This adjustment has already been computed.return app.curRawAdj;}//设置empty进程adjif (app.thread == null) {app.adjSeq = mAdjSeq;app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;//空进程app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);}//初始化一些变量//这些变量的具体用途,我们不关注//大家只用留意一下ProcessRecord的schedGroup、procState和oom_adj即可app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;app.adjSource = null;app.adjTarget = null;app.empty = false;app.cached = false;final int activitiesSize = app.activities.size();//初始化ProcessRecord maxAdjUNKNOWN_ADJ,//maxAdj取值为UNKNOWN_ADJ,即最大的1001//但是应该是只有系统进程才有可能进入这个分支。系统进程走完这个分支就返回了if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {//adjType 跟cpu的调度权限有关系app.adjType = "fixed";app.adjSeq = mAdjSeq;app.curRawAdj = app.maxAdj;app.foregroundActivities = false;app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;//// System processes can do UI, and when they do we want to have// them trim their memory after the user leaves the UI.  To// facilitate this, here we need to determine whether or not it// is currently showing UI.app.systemNoUi = true;if (app == TOP_APP) {app.systemNoUi = false;} else if (activitiesSize > 0) {for (int j = 0; j < activitiesSize; j++) {final ActivityRecord r = app.activities.get(j);if (r.visible) {app.systemNoUi = false;}}}if (!app.systemNoUi) {//系统进程有ui的时候app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;}//return (app.curAdj=app.maxAdj);}app.systemNoUi = false;//xxx
}

第一部分主要是更新了empty 进程与系统进程的curProcState。

第二部分

        // Determine the importance of the process, starting with most// important to least, and assign an appropriate OOM adjustment.int adj;int schedGroup;int procState;boolean foregroundActivities = false;BroadcastQueue queue;if (app == TOP_APP) {//若进程包含正在前台显示的Activityadj = ProcessList.FOREGROUND_APP_ADJ;//schedGroup = Process.THREAD_GROUP_DEFAULT;app.adjType = "top-activity";foregroundActivities = true;procState = ActivityManager.PROCESS_STATE_TOP;} else if (app.instrumentationClass != null) {// Don't want to kill running instrumentation.adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = Process.THREAD_GROUP_DEFAULT;app.adjType = "instrumentation";procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;} else if ((queue = isReceivingBroadcast(app)) != null) {// An app that is currently receiving a broadcast also// counts as being in the foreground for OOM killer purposes.// It's placed in a sched group based on the nature of the// broadcast as reflected by which queue it's active in.adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = (queue == mFgBroadcastQueue)? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;app.adjType = "broadcast";procState = ActivityManager.PROCESS_STATE_RECEIVER;} else if (app.executingServices.size() > 0) {// An app that is currently executing a service callback also// counts as being in the foreground.adj = ProcessList.FOREGROUND_APP_ADJ;schedGroup = app.execServicesFg ?Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;app.adjType = "exec-service";procState = ActivityManager.PROCESS_STATE_SERVICE;//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);} else {// As far as we know the process is empty.  We may change our mind later.schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;// At this point we don't actually know the adjustment.  Use the cached adj// value that the caller wants us to.//这一句很重要,除了上面几种情形,先设置adj为cacheAdj,也即是UNKNOWN_ADJadj = cachedAdj;//表明此时进程处于后台procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;app.cached = true;app.empty = true;app.adjType = "cch-empty";}
上述几个分支除了最后的else 分支其余的adj都是FOREGROUND_APP_ADJ,但是schedGroup与procState 不同,也就是cpu资源调度是不一样的。当进程正在显示或者正在测试或者正在执行Service 或者正在执行Receiver的时候此时设置adj 为FOREGROUND_APP_ADJ。FOREGROUND_APP_ADJ 实际是0,比他小的都是adj一般表示的都是系统进程,因此FOREGROUND_APP_ADJ 可以看成是普通用户可以使用的最高的adj,用于这个adj的进程内存几乎不会被回收。最后的else 分支是将先将adj 设置为cachedAdj,此处cachedAdj 为UNKNOWN_ADJ,实际就是在后面再根据情况决定adj的值为多少。如果computeOomAdjLocked 执行完了,这个进程的adj 还是UNKNOWN_ADJ,此时会在updateOomAdjLocked 方法中将adj 设置为ProcessList.CACHED_APP_MIN_ADJ到ProcessList.CACHED_APP_MAX_ADJ之间的某一个值。详情参见上一篇文章。

第三部分

        //第三部分处理包含Activity的进程时// //依次轮询进程中的Activity,app的adj会由if (!foregroundActivities && activitiesSize > 0) {for (int j = 0; j < activitiesSize; j++) {final ActivityRecord r = app.activities.get(j);if (r.app != app) {continue;}//如果进程包含可见Activity,即该进程是个可见进程,if (r.visible) {//更新状态,原因是在for循环里面可能先走了后面的分支,然后再进入了本分支//这样最终进程的adj就由最重要的activity的状态决定了。if (adj > ProcessList.VISIBLE_APP_ADJ) {//adj大于VISIBLE_APP_ADJ时,才更新对应的adj//之前提到的正在处理广播、服务或测试的进程(也就是第一部分),adj为FOREGROUND,是小于VISIBLE_APP_ADJ//因此不会在此更新,adj = ProcessList.VISIBLE_APP_ADJ;app.adjType = "visible";}if (procState > ActivityManager.PROCESS_STATE_TOP) {procState = ActivityManager.PROCESS_STATE_TOP;}//正在处理广播、服务或测试的进程,如果它们的调度策略为BACKGROUND//但又包含了可见Activity时,调度策略变更为DEFAULTschedGroup = Process.THREAD_GROUP_DEFAULT;app.cached = false;app.empty = false;foregroundActivities = true;//注意break了//发现可见Activity时,直接可以结束循环break;} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {//如果进程包含处于PAUSING或PAUSED状态的Activity时//将其oom_adj调整为“用户可察觉”的的等级,这个等级还是很高的if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {adj = ProcessList.PERCEPTIBLE_APP_ADJ;app.adjType = "pausing";}if (procState > ActivityManager.PROCESS_STATE_TOP) {procState = ActivityManager.PROCESS_STATE_TOP;}schedGroup = Process.THREAD_GROUP_DEFAULT;app.cached = false;app.empty = false;foregroundActivities = true;//注意并不会break} else if (r.state == ActivityState.STOPPING) {//包含处于Stopping状态Activity的进程,其oom_adj也被置为PERCEPTIBLE_APP_ADJif (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {adj = ProcessList.PERCEPTIBLE_APP_ADJ;app.adjType = "stopping";}if (!r.finishing) {if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;}}app.cached = false;app.empty = false;foregroundActivities = true;} else {//只是含有cached-activity的进程,仅调整procStateif (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;app.adjType = "cch-act";}}}}

这一部分主要是遍历进程当中的所有页面,每一个页面都有一个adj,而最终进程的adj 选取其中最小的值。例如一个进程有一个可见页面有一个正在执行onPause 的页面,那么进程的adj 是ProcessList.VISIBLE_APP_ADJ,再比例进程有一个正在执行onPause的页面以及一个正在执行onStop 的页面,此时进程的adj 是ProcessList.PERCEPTIBLE_APP_ADJ。

如果没有可见页面,也不是正在执行onPause 以及onStop 的页面,就是已经处于后台的将进程,,此时adj 为cacheAdj。  参见第二部的else 分支。

第四部分

        //第四部分主要用于处理一些特殊的进程。if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {//进程包含前台服务或被强制在前台运行时//oom_adj被调整为PERCEPTIBLE_APP_ADJ,只是procState略有不同if (app.foregroundServices) {// 当前进程包含前台服务adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;app.cached = false;app.adjType = "fg-service";schedGroup = Process.THREAD_GROUP_DEFAULT;} else if (app.forcingToForeground != null) {//例如当前进程正在弹出一个Toast。(是否可以使用定时Toast来保活)adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;app.cached = false;app.adjType = "force-fg";app.adjSource = app.forcingToForeground;schedGroup = Process.THREAD_GROUP_DEFAULT;}}///AMS的HeavyWeight进程单独处理if (app == mHeavyWeightProcess) {if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {// We don't want to kill the current heavy-weight process.adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;app.cached = false;app.adjType = "heavy";}if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;}}//home进程特殊处理if (app == mHomeProcess) {if (adj > ProcessList.HOME_APP_ADJ) {adj = ProcessList.HOME_APP_ADJ;schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;app.cached = false;app.adjType = "home";}if (procState > ActivityManager.PROCESS_STATE_HOME) {procState = ActivityManager.PROCESS_STATE_HOME;}}//前台进程之前的一个进程,也就是上次显示的进程if (app == mPreviousProcess && app.activities.size() > 0) {if (adj > ProcessList.PREVIOUS_APP_ADJ) {// This was the previous process that showed UI to the user.// We want to try to keep it around more aggressively, to give// a good experience around switching between two apps.adj = ProcessList.PREVIOUS_APP_ADJ;schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;app.cached = false;app.adjType = "previous";}if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;}}if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj+ " reason=" + app.adjType);// By default, we use the computed adjustment.  It may be changed if// there are applications dependent on our services or providers, but// this gives us a baseline and makes sure we don't get into an// infinite recursion.app.adjSeq = mAdjSeq;app.curRawAdj = adj;app.hasStartedServices = false;//处理正在进行backup工作的进程if (mBackupTarget != null && app == mBackupTarget.app) {// If possible we want to avoid killing apps while they're being backed upif (adj > ProcessList.BACKUP_APP_ADJ) {if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);adj = ProcessList.BACKUP_APP_ADJ;if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;}app.adjType = "backup";app.cached = false;}if (procState > ActivityManager.PROCESS_STATE_BACKUP) {procState = ActivityManager.PROCESS_STATE_BACKUP;}}

这里以及前面三个部分在处理adj的时候都会先判断一下adj的大小,例如本部分的第一行

adj > ProcessList.PERCEPTIBLE_APP_ADJ。这里这句代码的主要作用是过滤掉前面已经设置了adj的进程。

因为computeOomAdjLocked 里面设置adj 是按照由小到大的顺序处理的,因此在前面部分被设置了某个非UNKNOWN_ADJ的值一般后续比较adj大于某个值的条件都不在处理。

这一部分主要处理了一些特殊的进程,如执行前台Service 的进程,桌面进程,前一次显示的进程等等。

第五部分

    boolean mayBeTop = false;for (int is = app.services.size()-1;is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE|| procState > ActivityManager.PROCESS_STATE_TOP);is--) {ServiceRecord s = app.services.valueAt(is);//通过startService启动过Serviceif (s.startRequested) {app.hasStartedServices = true;if (procState > ActivityManager.PROCESS_STATE_SERVICE) {procState = ActivityManager.PROCESS_STATE_SERVICE;}//app之前启动过Activityif (app.hasShownUi && app != mHomeProcess) {if (adj > ProcessList.SERVICE_ADJ) {app.adjType = "cch-started-ui-services";}} else {//MAX_SERVICE_INACTIVITY为activity启动service后,系统最多保留Service的时间。//没有启动过Activity,并且30分钟之内活跃过的服务进程if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {if (adj > ProcessList.SERVICE_ADJ) {//虽然处于后台,相当于提高了adj的级别,//app.hasShownUi && app != mHomeProcess 这个分支内部内有提高adj级别//说明一个纯粹的服务进程的优先级是高于既有ui又有服务的进程的adj = ProcessList.SERVICE_ADJ;app.adjType = "started-services";app.cached = false;}}//处理Service存在超时的情况,可见超时时也不会调整oom_adjif (adj > ProcessList.SERVICE_ADJ) {app.adjType = "cch-started-services";}}}//bindService 启动的Service。for (int conni = s.connections.size()-1;conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE|| procState > ActivityManager.PROCESS_STATE_TOP);conni--) {ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);//多次调用bind多次调用bindService 绑定同一个Service,每次绑定的//参数可能不一样。for (int i = 0;i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE|| procState > ActivityManager.PROCESS_STATE_TOP);i++) {// XXX should compute this based on the max of// all connected clients.ConnectionRecord cr = clist.get(i);if (cr.binding.client == app) {// Binding to ourself is not interesting.continue;}//bingService时添加了BIND_WAIVE_PRIORITY这个flag。if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {// client 是调用bindService 的进程,也就是客户端。ProcessRecord client = cr.binding.client;//计算出客户端进程的oom_adj//由此可看出Android oom_adj的计算多么麻烦int clientAdj = computeOomAdjLocked(client, cachedAdj,TOP_APP, doingAll, now);int clientProcState = client.curProcState;if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {// If the other app is cached for any reason, for purposes here// we are going to consider it empty.  The specific cached state// doesn't propagate except under certain conditions.clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;}String adjType = null;//BIND_ALLOW_OOM_MANAGEMENT置为1时,先按照通常的处理方式,调整服务端进程的adjTypeif ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {if (app.hasShownUi && app != mHomeProcess) {if (adj > clientAdj) {adjType = "cch-bound-ui-services";}app.cached = false;//当前进程展示过ui, 会将clientAdj修改为当前进程的adj,clientAdj = adj;clientProcState = procState;} else {if (now >= (s.lastActivity+ ActiveServices.MAX_SERVICE_INACTIVITY)) {if (adj > clientAdj) {adjType = "cch-bound-services";}//服务已经好长时间没有执行过了,那么同样 会将clientAdj修改为当前进程的adjclientAdj = adj;}//若是距离服务上次活跃时间还没有超过30min,那么adj 放到后面修改}}//当前进程优先级低于客户端//adj>clientAdj 表明client此时可能是正在显示或是可见的页面,而当前进程是后台进程if (adj > clientAdj) {if (app.hasShownUi && app != mHomeProcess&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {adjType = "cch-bound-ui-services";} else {//这里主要是处理client 的adj 大于当前进程adj的情形。//例如进程A 绑定了另一个进程B 内的一个Service。//此时可能A进程为当前正在显示的进程而B 是一个后台进程。//此时可以适当提高一个进程B的adj。//例如B本来是CACHED_APP_MAX_ADJ但由于A 是可见进程,//此时B 的adj 也会是可见进程。//bindService 同时添加了BIND_ABOVE_CLIENT与BIND_IMPORTANTif ((cr.flags&(Context.BIND_ABOVE_CLIENT|Context.BIND_IMPORTANT)) != 0) {//adj 最小取值是PERSISTENT_SERVICE_ADJ。adj = clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ? clientAdj : ProcessList.PERSISTENT_SERVICE_ADJ;} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {//BIND_NOT_VISIBLE表示不将服务端当作visible进程看待adj = ProcessList.PERCEPTIBLE_APP_ADJ;} else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {//例如客户端是PERCEPTIBLE_APP_ADJ,这里一般也是提升//adj 的级别adj = clientAdj;} else {if (adj > ProcessList.VISIBLE_APP_ADJ) {adj = ProcessList.VISIBLE_APP_ADJ;}}if (!client.cached) {app.cached = false;}adjType = "service";}}// adj <=clientAdj,例如客户端是后台进程,当前进程为正在显示的进程。//if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {//BIND_NOT_FOREGROUND表示系统将阻止驻留该服务的进程具有前台优先级if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {schedGroup = Process.THREAD_GROUP_DEFAULT;}if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {if (clientProcState == ActivityManager.PROCESS_STATE_TOP) {// Special handling of clients who are in the top state.// We *may* want to consider this process to be in the// top state as well, but only if there is not another// reason for it to be running.  Being on the top is a// special state, meaning you are specifically running// for the current top app.  If the process is already// running in the background for some other reason, it// is more important to continue considering it to be// in the background state.mayBeTop = true;//降低clientProcStateclientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;} else {// Special handling for above-top states (persistent// processes).  These should not bring the current process// into the top state, since they are not on top.  Instead// give them the best state after that.//也是降低clientProcStateclientProcState =ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;}}} else {if (clientProcState <ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {clientProcState =ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;}}if (procState > clientProcState) {procState = clientProcState;}if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND&& (cr.flags&Context.BIND_SHOWING_UI) != 0) {app.pendingUiClean = true;}if (adjType != null) {app.adjType = adjType;app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE;app.adjSource = cr.binding.client;app.adjSourceProcState = clientProcState;app.adjTarget = s.name;}}//看成是activityif ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {app.treatLikeActivity = true;}// 根据当前进程activity来调整adj和schedGroupfinal ActivityRecord a = cr.activity;//a 非空表示Service 是从页面a 启动。//&Context.BIND_ADJUST_WITH_ACTIVITY是指当从Activity绑定到该进程时,// 允许目标服务进程根据该activity的可见性来提升优先级if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&(a.visible || a.state == ActivityState.RESUMED|| a.state == ActivityState.PAUSING)) {//提高服务进程的优先级adj = ProcessList.FOREGROUND_APP_ADJ;if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {schedGroup = Process.THREAD_GROUP_DEFAULT;}app.cached = false;app.adjType = "service";app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE;app.adjSource = a;app.adjSourceProcState = procState;app.adjTarget = s.name;}}}}}

这里主要是处理进程内运行了Service的情况,根据Service的启动情形有分为两种情况。

通过startService启动:

        这里Service 所在进程没有启动父ui 并且服务在30分钟活跃过,那么adj 为ProcessList.SERVICE_ADJ,其余的情形的adj 依然是在前面设置的PROCESS_STATE_CACHED_ACTIVITY。换句话说一个Service 在一定时间不在运行之后就会无效。

通过bindService 启动:

       这里主要是根据bindService 提供的Flag 来适当的提高Service 进程的adj,使得服务进程的内存没有那么容易被回收。具体Flag 的意义大家可以查询相关资料。

假设一个进程既有activity 又有Service 那么该进程的adj 取何值?

该Service 是starService 且没有被bindService绑定过,同时进程内没有ContentProvider。

由上图可以看到当一个服务进程展示过ui的情况下会修改adjType,但是没有给adj 赋值,此时adj 依然是前面设置的UNKNOW_ADJ。

updateOomAdjLocked

updateOomAdjLocked 通过computeOomAdjLocked 修改进程的adj 之后会判断进程的adj是否大于等于UNKNOWN_ADJ, 一个有ui的Service 进程的adj 是UNKNOWN_ADJ,正好满足条件

之后进程app 的adj 会被设置为CACHED_APP_MIN_ADJ到CACHED_APP_MAX_ADJ 之间的某一个值。

一般启动了服务的进程往往是希望服务在后台能够执行某些任务,这样看是不希望这些服务因为进程被杀而过早的被终止的。正确的做法是,对于期望较长时间留在后台的服务,应该将服务运行在单独的进程里,即是UI进程与Servie进程分离,这样期望长时间留在后台的Serivce会获得较小的Adj值,而占有大量内存的UI进程则会分类为Cached(后台)进程,能够在需要的时候更快地被回收。

第六部分

省略,这一部分主要是根据ContentProivder 来设置adj,逻辑类似Service。

第七部分

        //第七部分if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {// A client of one of our services or providers is in the top state.  We// *may* want to be in the top state, but not if we are already running in// the background for some other reason.  For the decision here, we are going// to pick out a few specific states that we want to remain in when a client// is top (states that tend to be longer-term) and otherwise allow it to go// to the top state.switch (procState) {case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:case ActivityManager.PROCESS_STATE_SERVICE:// These all are longer-term states, so pull them up to the top// of the background states, but not all the way to the top state.procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;break;default:// Otherwise, top is a better choice, so take it.procState = ActivityManager.PROCESS_STATE_TOP;break;}}//if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {if (app.hasClientActivities) {// This is a cached process, but with client activities.  Mark it so.procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;app.adjType = "cch-client-act";} else if (app.treatLikeActivity) {// This is a cached process, but somebody wants us to treat it like it has// an activity, okay!// 将Service 看成是activity .procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;app.adjType = "cch-as-act";}}//        对Service进程做一些特殊处理if (adj == ProcessList.SERVICE_ADJ) {if (doingAll) {//每次updateOomAdj时,将mNewNumAServiceProcs置为0//然后LRU list中,从后往前数,前1/3的service进程就是AService//其余的就是bService//mNumServiceProcs为上一次update时,service进程的数量//mNewNumAServiceProcs 表示当前处理的第几个Serviceapp.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);mNewNumServiceProcs++;//Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);if (!app.serviceb) {// 如果不是bService,但内存回收等级过高,也被视为bServiceif (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL&& app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {app.serviceHighRam = true;app.serviceb = true;//Slog.i(TAG, "ADJ " + app + " high ram!");} else {mNewNumAServiceProcs++;//Slog.i(TAG, "ADJ " + app + " not high ram!");}} else {app.serviceHighRam = false;}}if (app.serviceb) {//LRU中后1/3的Service,都是AServiceadj = ProcessList.SERVICE_B_ADJ;}}//计算完毕app.curRawAdj = adj;//if基本没有用,maxAdj已经是最大的UNKNOW_ADJif (adj > app.maxAdj) {adj = app.maxAdj;if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {schedGroup = Process.THREAD_GROUP_DEFAULT;}}//最后做一些记录和调整app.curAdj = app.modifyRawOomAdj(adj);app.curSchedGroup = schedGroup;app.curProcState = procState;app.foregroundActivities = foregroundActivities;return app.curRawAdj;

这一部分主要是根据前面计算修改一些进程的proState以及根据服务在进程列表的位置修改adj。

所有的进程保存在一个列表里面,从后往前数,前1/3的service进程就是AService

总结

这篇关于android 内存管理之adj 《三》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/700338

相关文章

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR