Android性能优化OOM内存管理ADJ
前言
OOM_ADJ (Out of Memory Adjustment)是android系统在内存不足情况下进行内存调整的重要参数。在处理app启动速度的时候,可以设置主线程的优先级,保证主线程占用的cpu足够久。进程的oom_adj,决定了当内存不够的时候,lmk会根据oom_adj的大小依次释放内存。在前面介绍Activity页面启动路程过程中见到了更新adj的相关方法,但是没有深入介绍,这里分析一些相关实现。 更新adjfinal boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { r.startFreezingScreenLocked(app, 0); //更新Lur mService.updateLruProcessLocked(app, true, null); //更新ADJ mService.updateOomAdjLocked(); xxxx //通过Binder 远程调用Activity的onCreate onResume等生命周期 app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); return true; }
在启动页面的流程中存在一个名为realStartActivityLocked的方法,这个方法会通过Binder 远程调用Activity的onCreate,onResume 等生命周期方法,在回调生命周期之前调用了updateLruProcessLocked以及updateOomAdjLocked 这两个方法。这两个方法都与进程的优先级有关系。 updateLruProcessLocked final void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) { //hasActivity用来表示某个app中是否包含activity组件 //1.app本身确实包含activity组件; //2. app本身有service,并且有另外一个含有activity的app链接到此app的service上; //3. 该app启动serivce的时候带有标记BIND_TREAT_LIKE_ACTIVITY。 final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities || app.treatLikeActivity; //目前,并没有考虑进程中是否含有Service //因此,虽然理论上定义了Service相关的进程分类,但并没有实现对应的管理策略 //在以下代码中,hasService一直为false final boolean hasService = false; // not impl yet. app.services.size() > 0; if (!activityChange && hasActivity) { // The process has activities, so we are only allowing activity-based adjustments // to move it. It should be kept in the front of the list with other // processes that have activities, and we don"t want those to change their // order except due to activity operations. return; } //计数器,记录该函数被调用了多少次,也就是LRU被更新了多少次。 mLruSeq++; final long now = SystemClock.uptimeMillis(); app.lastActivityTime = now; // First a quick reject: if the app is already at the position we will // put it, then there is nothing to do. if (hasActivity) { final int N = mLruProcesses.size(); //如果要插入的app已经在mLruProcesses顶端了,就不用插入了 if (N > 0 && mLruProcesses.get(N-1) == app) { if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top activity: " + app); return; } } else { //将其插入到Service 的开头 if (mLruProcessServiceStart > 0 && mLruProcesses.get(mLruProcessServiceStart-1) == app) { if (DEBUG_LRU) Slog.d(TAG, "Not moving, already top other: " + app); return; } } int lrui = mLruProcesses.lastIndexOf(app); // persistent app,这部分app不会被杀死,永远在运行, // if (app.persistent && lrui >= 0) { //如果persistent app 已经在列表里面了那么不作处理。 // We don"t care about the position of persistent processes, as long as // they are in the list. if (DEBUG_LRU) Slog.d(TAG, "Not moving, persistent: " + app); return; } //lrui>=0,说明LRU中之前记录过当前进程的信息 //即该进程不是新创建的 //那么在调整之前,需要先将之前的记录删除 if (lrui >= 0) { if (lrui < mLruProcessActivityStart) { //此进程没有包含Activity mLruProcessActivityStart--; } if (lrui < mLruProcessServiceStart) { //此进程没有服务,是个其他类型的进程 mLruProcessServiceStart--; } //移除进程,后面会再次添加 mLruProcesses.remove(lrui); } //nextIndex主要用于记录 //当前进程绑定的Service或ContentProvider对应的进程, //应该插入的位置 (对应进程中仅含有Service和Provider时才需要处理) //后文将看到该值的使用情况 int nextIndex; if (hasActivity) { final int N = mLruProcesses.size(); if (app.activities.size() == 0 && mLruProcessActivityStart < (N-1)) { //该App没有Activity, 但是有一个有Activity的app启动了该App的一个Service。 //mLruProcessActivityStart < (N-1) 表示App 不是当前在显示的页面。 mLruProcesses.add(N-1, app); //举一个具体的例子,当前显示的App A 打开属于另一个App B的Service,此时当前显示的App A就在 //N 这个位置,被打开的Service 所在的App B在N-1 这个位置。 // To keep it from spamming the LRU list (by making a bunch of clients), // we will push down any other entries owned by the app. // 下面的代码,是为了调整不同用户之间的公平性; // 当前用户新启动了一个进程,将该用户对应的其它进程,适当往前挪动一下 (优先被kill) final int uid = app.info.uid; // 为了防止某个app中的service绑定了一群client从而导致LRU中顶部大部分都是这些client //,这里需要将这些client往下移动,以防止某些app通过和某个app的service绑定从而提升自己在LRU中位置。 for (int i=N-2; i>mLruProcessActivityStart; i--) { ProcessRecord subProc = mLruProcesses.get(i); //遍历找到第一个与app 的uid if (subProc.info.uid == uid) { if (mLruProcesses.get(i-1).info.uid != uid) { //交换i与i-1位置的进程, ProcessRecord tmp = mLruProcesses.get(i); mLruProcesses.set(i, mLruProcesses.get(i-1)); mLruProcesses.set(i-1, tmp); i--; } //还是以上面那个例子为例。 A 在打开B之后有打开另一个App C 的Service。 //此时 A,B,C 的位置是 N,N-1,N-2 ,由于B C的uid 一样, //此时B也就是先打开的服务可能会一直向后移动直到mLruProcessActivityStart这个位置, } else { // A gap, we can stop here. break; } } } else { // Process has activities, put it at the very tipsy-top. if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU activity list: " + app); //进程具有activity,在N位置添加,也就是在栈顶添加,此时app 一般就是要显示的app。 mLruProcesses.add(app); } nextIndex = mLruProcessServiceStart; } else if (hasService) { // Process has services, put it at the top of the service list. //不走这个分支,hasService 总是false, if (DEBUG_LRU) Slog.d(TAG, "Adding to top of LRU service list: " + app); mLruProcesses.add(mLruProcessActivityStart, app); nextIndex = mLruProcessServiceStart; mLruProcessActivityStart++; } else { // Process not otherwise of interest, it goes to the top of the non-service area. // 一般走这里, int index = mLruProcessServiceStart; //一般情况下client == null, 这个分支不走 if (client != null) { //client 表示一个另一个进程,此进程可能具有页面,也可没有,但是这个进程打开了 //一个只有服务得进程,那么只有服务的进程需要排在client进程的下面 // If there is a client, don"t allow the process to be moved up higher // in the list than that client. int clientIndex = mLruProcesses.lastIndexOf(client); if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating " + app); if (clientIndex <= lrui) { // Don"t allow the client index restriction to push it down farther in the // list than it already is. clientIndex = lrui; } if (clientIndex >= 0 && index > clientIndex) { //此时表示client 也是一个只有服务的进程而且client在app进程的下面,此时需要 //调整添加app进程的位置,调整之后app的位置是clientIndex,client的位置是clientIndex+1 index = clientIndex; } } if (DEBUG_LRU) Slog.d(TAG, "Adding at " + index + " of LRU list: " + app); //添加进程 mLruProcesses.add(index, app); nextIndex = index-1; mLruProcessActivityStart++; mLruProcessServiceStart++; } // If the app is currently using a content provider or service, // bump those processes as well. //本进程打开了service或者是ContentProvider,如果这个Service或者ContentProvider // 是定义自己App 里面那么此处没啥影响。如果是定义在另一个App里面则有影响。 //这里的微调分为两种情况: //第一是service所在的进程的位置调整到本进程之后, //第二是将ContentProvider所在的进程位置调整到本进程之后。 //调整的方式都是使用updateLruProcessInternalLocked方法, for (int j=app.connections.size()-1; j>=0; j--) { ConnectionRecord cr = app.connections.valueAt(j); if (cr.binding != null && !cr.serviceDead && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq && !cr.binding.service.app.persistent) { nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex, "service connection", cr, app); } } for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) { nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, "provider reference", cpr, app); } } } private final int updateLruProcessInternalLocked(ProcessRecord app, long now, int index, String what, Object obj, ProcessRecord srcApp) { //srcApp 打开 app 的一个Service 或者ContentProvider app.lastActivityTime = now; //如果有Activity,不做调整 if (app.activities.size() > 0) { // Don"t want to touch dependent processes that are hosting activities. return index; } //如果进程不在mLruProcess中,就返回 int lrui = mLruProcesses.lastIndexOf(app); if (lrui < 0) { Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: " + what + " " + obj + " from " + srcApp); return index; } //如果进程的位置高于需要调整的位置,不做调整 if (lrui >= index) { // Don"t want to cause this to move dependent processes *back* in the // list as if they were less frequently used. return index; } //如果目前进程的位置比mLruProcessActivityStart还要高,不调整 if (lrui >= mLruProcessActivityStart) { // Don"t want to touch dependent processes that are hosting activities. return index; } //走到这里表示lrui 0) { index--; } if (DEBUG_LRU) Slog.d(TAG, "Moving dep from " + lrui + " to " + index + " in LRU list: " + app); // mLruProcesses.add(index, app); return index; } // 例如 当前显示的App A打开了一个App B 的一个Service ,由于App A 是当前显示的App,优先级最高, //此时A 使用的Service 所在的App B 也应该尽可能的提高等级避免内存回收,此时会将App B 放到mLruProcessServiceStart 这个位置。 //假如非得回收内存的话会先回收0-mLruProcessServiceStart 之间的进程占据的内存。
mLruProcesses 是一个列表,其本分为三个部分 0--mLruProcessServiceStart 用于保存其他进程;
mLruProcessServiceStart -- mLruProcessActivityStart 用于保存服务进程,但是实际情况下这个区域的大小是0,也即是服务进程实际也是放在了其他进程区域。
mLruProcessActivityStart--end 保存的有Activity的进程。 每次添加Activity 进程都是在end位置,在mLruProcessServiceStart位置添加服务进程或者其他进程。
位置越大的进程优先级越高越不容易被回收。
每次调用updateLruProcessLocked调整某个进程的位置的时候也会调整与之相关的进程的位置,例如调整进程A 的位置 就要顺便调整A 启动的Service 以及ContentProvider 所在的进程位置。
————————————————
版权声明:本文为CSDN博主「昨夜西风在吹」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_31469589/article/details/11796935
以上就是有关Android内存管理ADJ讲解;有关更多Android 开发技术性能调优学习; 大家私信:"手册" 《Android性能优化手册》 获取相关学习资料。 结尾(心灵的鸡汤)
我相信,梦想只要能坚持,就一定能成为现实。就像代表着永恒的天蓝色。就让这小小的梦想的种子,在我们心中,渐渐发芽、成长,在心中开出美丽、绚烂的花。让我们努力飞翔,乘着梦想的翅膀,飞到成功的远方。
女死刑犯在最后一晚,都做些什么事?女死刑犯在最后一晚,又可以做些什么。除了整晚辗转反侧,沉默寡言,喃喃自语,还能怎么样,生命的权利以不掌握在自己手中,当初的残忍血腥,在这一刻唤醒泯灭人性的良知,但手铐脚缭以牢牢套着
衢州房价会跌吗?跌的概率是很大的,必尽衢州人均收入跟房价是不成正比的,衢州本地人口就几十万等级,也就4线城市,衢州外来人口又不多,本区域又没有大的产业经济支撑,西区房子都跟航埠接上了,整体房子空置
农民工没有社保,都不愿个人缴费以后怎么办,如果个人缴费划算吗?社保是缴才有,不缴就没有。这是社保法规定的。自己不愿意缴,以后要自己想办法,伸手要办不到。自己缴还是划算的,可以享受国家的补贴,不缴享受不到。不愿意交就没有社保呗,有啥啊划算?那要
跟邹扶澜老师视频自学欧楷一年半多了,现在感觉不到进步了,该怎么办?跟邹扶澜的视频练习书法写成这样已经很好了,写的都比邹扶澜好了,比邹的欧体写的还纯正呢,再跟着学下去当然就没有进步了,写成这样了就不用再看邹扶澜的视频了,赶紧关掉视频吧。客观来说,邹
评职称时优秀班集体对普通老师有用吗?评职称时优秀班集体有用没用,关键看你们单位职称评审方案是如何界定的。如果评审方案有这方面的规定,那就可以给加分。如果没涉及到就不可能给加分了。首先,优秀班集体是集体荣誉,不是个人荣
什么情况下,子女不能当兵,考公务员?我国法律法规,无论当兵还是考公务员,都是需要政审的,而且政审审查的就是三代内的直系亲属的政治问题。不管父母因为何种原因,触犯了法律,被判刑坐牢,那么子女当兵或者是考公务员都是过不了
从十元钱解决看病难,到一百八十一元一人的新农合还值不值得交这个钱?2018年的新农合费用已经开始征收了,相比2017年,2018年的新农合会有较明显的涨幅,多数地区都已经从2016年的150元每人上涨到了180元每人。当然,由于各地区新农合的缴费
彩票怎样买中奖率高?中奖简单,中大奖很难。我以前经常买彩票,摸索出一点小窍门,给大家分享。虽然中不了大奖,但是不会赔很多。以提主所说的福利彩票双色球为例。双色球的玩法是从33个红球中选择6个,从16个
航空乘务员的年薪加年终奖,一年是多少,福利又是多少呢?这个问题比较笼统了,航空乘务员内部也是分很多级别的,甚至其中还有拿年薪的领导阶层,同时不同的航空公司计算方式还略有不同。咱就不讨论刚入职以及那些拿年薪平均小时的领导阶层了。入职五年
打算读博的话,研究生毕业学校重要吗?读博有三种方式,一是考博,二是硕博连读,三是直博。想要读博士研究生毕业学校重要吗?如果是考博的话,研究生毕业院校有一定的影响,但如果是硕博连读,那研究生毕业学校没有什么影响。考博与
有的退休老人的退休金八九千元,为什么消费的时候不敢大手大脚?谢邀请,有多种原因在内,当年过的穷日子,有些害怕,也有的省吃俭用养成习惯,有的为了以后身体有个疾病,准备治疗底垫,加上消费观念不同,真的有钱不敢花不敢用,抠门过日子是他们的优良传统