Android性能优化OOM内存管理ADJ
前言
OOMADJ(OutofMemoryAdjustment)是android系统在内存不足情况下进行内存调整的重要参数。在处理app启动速度的时候,可以设置主线程的优先级,保证主线程占用的cpu足够久。进程的oomadj,决定了当内存不够的时候,lmk会根据oomadj的大小依次释放内存。在前面介绍Activity页面启动路程过程中见到了更新adj的相关方法,但是没有深入介绍,这里分析一些相关实现。更新adjfinalbooleanrealStartActivityLocked(ActivityRecordr,ProcessRecordapp,booleanandResume,booleancheckConfig)throwsRemoteException{r。startFreezingScreenLocked(app,0);更新LurmService。updateLruProcessLocked(app,true,null);更新ADJmService。updateOomAdjLocked();xxxx通过Binder远程调用Activity的onCreateonResume等生命周期app。thread。scheduleLaunchActivity(newIntent(r。intent),r。appToken,System。identityHashCode(r),r。info,newConfiguration(mService。mConfiguration),r。compat,r。task。voiceInteractor,app。repProcState,r。icicle,r。persistentState,results,newIntents,!andResume,mService。isNextTransitionForward(),profilerInfo);returntrue;}
在启动页面的流程中存在一个名为realStartActivityLocked的方法,这个方法会通过Binder远程调用Activity的onCreate,onResume等生命周期方法,在回调生命周期之前调用了updateLruProcessLocked以及updateOomAdjLocked这两个方法。这两个方法都与进程的优先级有关系。updateLruProcessLockedfinalvoidupdateLruProcessLocked(ProcessRecordapp,booleanactivityChange,ProcessRecordclient){hasActivity用来表示某个app中是否包含activity组件1。app本身确实包含activity组件;2。app本身有service,并且有另外一个含有activity的app链接到此app的service上;3。该app启动serivce的时候带有标记BINDTREATLIKEACTIVITY。finalbooleanhasActivityapp。activities。size()0app。hasClientActivitiesapp。treatLikeActivity;目前,并没有考虑进程中是否含有Service因此,虽然理论上定义了Service相关的进程分类,但并没有实现对应的管理策略在以下代码中,hasService一直为falsefinalbooleanhasServicefalse;notimplyet。app。services。size()0;if(!activityChangehasActivity){Theprocesshasactivities,soweareonlyallowingactivitybasedadjustmentstomoveit。Itshouldbekeptinthefrontofthelistwithotherprocessesthathaveactivities,andwedontwantthosetochangetheirorderexceptduetoactivityoperations。return;}计数器,记录该函数被调用了多少次,也就是LRU被更新了多少次。mLruSeq;finallongnowSystemClock。uptimeMillis();app。lastActivityTimenow;Firstaquickreject:iftheappisalreadyatthepositionwewillputit,thenthereisnothingtodo。if(hasActivity){finalintNmLruProcesses。size();如果要插入的app已经在mLruProcesses顶端了,就不用插入了if(N0mLruProcesses。get(N1)app){if(DEBUGLRU)Slog。d(TAG,Notmoving,alreadytopactivity:app);return;}}else{将其插入到Service的开头if(mLruProcessServiceStart0mLruProcesses。get(mLruProcessServiceStart1)app){if(DEBUGLRU)Slog。d(TAG,Notmoving,alreadytopother:app);return;}}intlruimLruProcesses。lastIndexOf(app);persistentapp,这部分app不会被杀死,永远在运行,if(app。persistentlrui0){如果persistentapp已经在列表里面了那么不作处理。Wedontcareaboutthepositionofpersistentprocesses,aslongastheyareinthelist。if(DEBUGLRU)Slog。d(TAG,Notmoving,persistent:app);return;}lrui0,说明LRU中之前记录过当前进程的信息即该进程不是新创建的那么在调整之前,需要先将之前的记录删除if(lrui0){if(lruimLruProcessActivityStart){此进程没有包含ActivitymLruProcessActivityStart;}if(lruimLruProcessServiceStart){此进程没有服务,是个其他类型的进程mLruProcessServiceStart;}移除进程,后面会再次添加mLruProcesses。remove(lrui);}nextIndex主要用于记录当前进程绑定的Service或ContentProvider对应的进程,应该插入的位置(对应进程中仅含有Service和Provider时才需要处理)后文将看到该值的使用情况intnextIndex;if(hasActivity){finalintNmLruProcesses。size();if(app。activities。size()0mLruProcessActivityStart(N1)){该App没有Activity,但是有一个有Activity的app启动了该App的一个Service。mLruProcessActivityStart(N1)表示App不是当前在显示的页面。mLruProcesses。add(N1,app);举一个具体的例子,当前显示的AppA打开属于另一个AppB的Service,此时当前显示的AppA就在N这个位置,被打开的Service所在的AppB在N1这个位置。TokeepitfromspammingtheLRUlist(bymakingabunchofclients),wewillpushdownanyotherentriesownedbytheapp。下面的代码,是为了调整不同用户之间的公平性;当前用户新启动了一个进程,将该用户对应的其它进程,适当往前挪动一下(优先被kill)finalintuidapp。info。uid;为了防止某个app中的service绑定了一群client从而导致LRU中顶部大部分都是这些client,这里需要将这些client往下移动,以防止某些app通过和某个app的service绑定从而提升自己在LRU中位置。for(intiN2;imLruProcessActivityStart;i){ProcessRecordsubProcmLruProcesses。get(i);遍历找到第一个与app的uidif(subProc。info。uiduid){if(mLruProcesses。get(i1)。info。uid!uid){交换i与i1位置的进程,ProcessRecordtmpmLruProcesses。get(i);mLruProcesses。set(i,mLruProcesses。get(i1));mLruProcesses。set(i1,tmp);i;}还是以上面那个例子为例。A在打开B之后有打开另一个AppC的Service。此时A,B,C的位置是N,N1,N2,由于BC的uid一样,此时B也就是先打开的服务可能会一直向后移动直到mLruProcessActivityStart这个位置,}else{Agap,wecanstophere。break;}}}else{Processhasactivities,putitattheverytipsytop。if(DEBUGLRU)Slog。d(TAG,AddingtotopofLRUactivitylist:app);进程具有activity,在N位置添加,也就是在栈顶添加,此时app一般就是要显示的app。mLruProcesses。add(app);}nextIndexmLruProcessServiceStart;}elseif(hasService){Processhasservices,putitatthetopoftheservicelist。不走这个分支,hasService总是false,if(DEBUGLRU)Slog。d(TAG,AddingtotopofLRUservicelist:app);mLruProcesses。add(mLruProcessActivityStart,app);nextIndexmLruProcessServiceStart;mLruProcessActivityStart;}else{Processnototherwiseofinterest,itgoestothetopofthenonservicearea。一般走这里,intindexmLruProcessServiceStart;一般情况下clientnull,这个分支不走if(client!null){client表示一个另一个进程,此进程可能具有页面,也可没有,但是这个进程打开了一个只有服务得进程,那么只有服务的进程需要排在client进程的下面Ifthereisaclient,dontallowtheprocesstobemoveduphigherinthelistthanthatclient。intclientIndexmLruProcesses。lastIndexOf(client);if(DEBUGLRUclientIndex0)Slog。d(TAG,Unknownclientclientwhenupdatingapp);if(clientIndexlrui){Dontallowtheclientindexrestrictiontopushitdownfartherinthelistthanitalreadyis。clientIndexlrui;}if(clientIndex0indexclientIndex){此时表示client也是一个只有服务的进程而且client在app进程的下面,此时需要调整添加app进程的位置,调整之后app的位置是clientIndex,client的位置是clientIndex1indexclientIndex;}}if(DEBUGLRU)Slog。d(TAG,AddingatindexofLRUlist:app);添加进程mLruProcesses。add(index,app);nextIndexindex1;mLruProcessActivityStart;mLruProcessServiceStart;}Iftheappiscurrentlyusingacontentproviderorservice,bumpthoseprocessesaswell。本进程打开了service或者是ContentProvider,如果这个Service或者ContentProvider是定义自己App里面那么此处没啥影响。如果是定义在另一个App里面则有影响。这里的微调分为两种情况:第一是service所在的进程的位置调整到本进程之后,第二是将ContentProvider所在的进程位置调整到本进程之后。调整的方式都是使用updateLruProcessInternalLocked方法,for(intjapp。connections。size()1;j0;j){ConnectionRecordcrapp。connections。valueAt(j);if(cr。binding!null!cr。serviceDeadcr。binding。service!nullcr。binding。service。app!nullcr。binding。service。app。lruSeq!mLruSeq!cr。binding。service。app。persistent){nextIndexupdateLruProcessInternalLocked(cr。binding。service。app,now,nextIndex,serviceconnection,cr,app);}}for(intjapp。conProviders。size()1;j0;j){ContentProviderRecordcprapp。conProviders。get(j)。provider;if(cpr。proc!nullcpr。proc。lruSeq!mLruSeq!cpr。proc。persistent){nextIndexupdateLruProcessInternalLocked(cpr。proc,now,nextIndex,providerreference,cpr,app);}}}privatefinalintupdateLruProcessInternalLocked(ProcessRecordapp,longnow,intindex,Stringwhat,Objectobj,ProcessRecordsrcApp){srcApp打开app的一个Service或者ContentProviderapp。lastActivityTimenow;如果有Activity,不做调整if(app。activities。size()0){Dontwanttotouchdependentprocessesthatarehostingactivities。returnindex;}如果进程不在mLruProcess中,就返回intlruimLruProcesses。lastIndexOf(app);if(lrui0){Slog。wtf(TAG,AddingdependentprocessappnotonLRUlist:whatobjfromsrcApp);returnindex;}如果进程的位置高于需要调整的位置,不做调整if(lruiindex){Dontwanttocausethistomovedependentprocessesbackinthelistasiftheywerelessfrequentlyused。returnindex;}如果目前进程的位置比mLruProcessActivityStart还要高,不调整if(lruimLruProcessActivityStart){Dontwanttotouchdependentprocessesthatarehostingactivities。returnindex;}走到这里表示lruiindexlruimLruProcessActivityStart。把App调整到index1的位置mLruProcesses。remove(lrui);if(index0){index;}if(DEBUGLRU)Slog。d(TAG,MovingdepfromlruitoindexinLRUlist:app);mLruProcesses。add(index,app);returnindex;}例如当前显示的AppA打开了一个AppB的一个Service,由于AppA是当前显示的App,优先级最高,此时A使用的Service所在的AppB也应该尽可能的提高等级避免内存回收,此时会将AppB放到mLruProcessServiceStart这个位置。假如非得回收内存的话会先回收0mLruProcessServiceStart之间的进程占据的内存。
mLruProcesses是一个列表,其本分为三个部分0mLruProcessServiceStart用于保存其他进程;
mLruProcessServiceStartmLruProcessActivityStart用于保存服务进程,但是实际情况下这个区域的大小是0,也即是服务进程实际也是放在了其他进程区域。
mLruProcessActivityStartend保存的有Activity的进程。每次添加Activity进程都是在end位置,在mLruProcessServiceStart位置添加服务进程或者其他进程。
位置越大的进程优先级越高越不容易被回收。
每次调用updateLruProcessLocked调整某个进程的位置的时候也会调整与之相关的进程的位置,例如调整进程A的位置就要顺便调整A启动的Service以及ContentProvider所在的进程位置。
版权声明:本文为CSDN博主昨夜西风在吹的原创文章,遵循CC4。0BYSA版权协议,转载请附上原文出处链接及本声明。原文链接:https:blog。csdn。netqq31469589articledetails11796935
以上就是有关Android内存管理ADJ讲解;有关更多Android开发技术性能调优学习;大家私信:手册《Android性能优化手册》获取相关学习资料。结尾(心灵的鸡汤)
我相信,梦想只要能坚持,就一定能成为现实。就像代表着永恒的天蓝色。就让这小小的梦想的种子,在我们心中,渐渐发芽、成长,在心中开出美丽、绚烂的花。让我们努力飞翔,乘着梦想的翅膀,飞到成功的远方。
威尔士公开赛,7场40宾汉姆出局,田鹏飞晋级丁俊晖今晚出战威尔士公开赛首日,绝大部分场次进行的是前16球员的延期资格赛。结果,比分悬殊的比赛较多,40一共有7场,41达到8场。7场408场41卫冕冠军乔佩里手感火热,40胜马克金火箭奥沙利
勇士队战胜奇才队,汤普森2745,科尔承诺他将参加背靠背比赛鹈鹕队开拓者队和森林狼队都获得比赛胜利,这三支球队的胜场相近,他们和勇士队是竞争对手,所以压力来到勇士队这边了,以目前的形势来看,至少有多支我们喜爱的球队将无缘季后赛,其中就可能包
记者足协或另聘主帅带亚运队,扬科维奇本应12强赛后期接班李铁直播吧2月14日讯据记者裴力报道,若扬科维奇执教国足,中国足协有可能聘请其他主帅带队征战亚运会的赛事。男足亚运队13日下午与乙级队丹东腾跃进行了一场教学赛,两队最终11战成平局。按
脾虚比肾虚或更可怕?常做4件事或有助健脾运脾导语肥胖可以说是我们迈向小康社会的绊脚石,过去,人们的经济条件很差,所以他们很瘦,如今,我们的生活条件越来越好,所以吃的食物狠丰富,这就导致了很多人出现肥胖的现象,可是肥胖会导给我
今天,互联网的瓜都在这了!1昨天,有网友咨询笔者。卦卦,有博主爆料了一条的新闻,这事是真的还是人造新闻?先给大家说结论,这事是真的。原始出处为某商业新媒体记者的私人社交软件。只不过,该记者还补充了一句还好演
老外拆解华为畅享50除了麒麟710A,其他自研芯片也不少在美方的封锁之下,华为手机遭遇了芯片危机,出货量迅速从巅峰期的2亿台,下降为千万台级别。2021年华为手机出货量仅为3000多万台,在2022年华为手机出货量占国内市场的7。9,按
抗癌抗氧化多吃红薯的好处多吃红薯的好处抗氧化剂丰富111并非所有的红薯都是橙色的。它们的皮肤和内部可以是白色黄色棕色红色粉红色和紫色。颜色的范围为餐桌带来了不同的营养。紫肉红薯被认为含有超高水平的抗氧化剂
武夷山五夫镇油菜花开迎春放百亩花田迎客来武夷山五夫镇百亩油菜花竞相绽放东南网记者郑正华摄2月18日,在武夷山五夫镇种植的百亩油菜花,提前竞相绽放,引得周边游客纷至沓来赏花游玩。百亩油菜花竞相绽放东南网记者郑正华摄游客打卡
乾安县余字乡张字村盐碱地里草莓甜来源吉林农业农村厅市县要闻走进乾安县余字乡张字村的宁一采摘园,阵阵草莓清香扑面而来,温室大棚的草莓迎来丰收季。不少市民慕名而来,体验采摘乐趣。这里的草莓除了满足游客采摘,还有一部分
T80文化国际街区周日闲逛,原本在下马坊博爱园这边逛,走了一条平时不走的小路,直接走过去才发现了这个地方,人很少,看了介绍才知道这个地方原来是中华民国在1934年建造的中央政治学院分院,1949年改
在这里,读懂超级网红的前世今生丨寻梦手记寻梦目的地威海市乳山市乳山牡蛎欢乐城景点特色牡蛎文化渔家文化老物件海鲜美食推荐打卡点牡蛎文化博物馆牡蛎文化园交通攻略自驾乘坐乳山201路K201路到华隆商厦下车,剩余70