资 讯

网站建设、app开发、微信开发、网络营销知识汇聚

We provide professional and all-round information services to enterprises from all levels and angles

详细解读android APP 开发中的 low memory killer机制
admin 2018-07-11


12.3 Low Memory Killer机制
Android借鉴 Linux进程管理机制,提供了自己的实现 LM Killer I( Low Memory Killero
与 Linux的 OOM Killer( Out Of Memory Killer)机制类似,它们都是在内存不足时,根据一
定的策略选择“杀死”一部分进程。通常可选的策略为 OOM adj,系统根据进程的ad值选择
哪些进程可以被“杀死"。
LM Killer与 OOM Killer I的不同之处在于: OOM Killer只有在内存不足时才选择“杀
死” OOM adj值较高的进程;而 LM Killer将 OOM adj划分为不同的等级,并为每个等级指
定了最小剩余内存阈值,当剩余内存小于该阈值时便可以“杀死”该等级内的进程。因此LM
Killer比 OOM Killer更灵活。
应用程序退出后,系统通常将其切换到后台,并不会“杀死”应用程序进程,这样加快
了下次启动该应用程序的速度,只有当内存不足或者低于某个阈值时,才会回收应用程序进
程。 LM Killer相对于直接“杀死”进程是一种被动的进程管理机制。
12.3.100Madj等级和最小内存阈值
LM Killer是 Android为 Linux i新增的驱动程序,其源码位于 kemel/drivers/ /staging/ /android
lowmemorykillerc I中,其中 OOM adj等级由/sys/ module/lowmemorykiller/parameters.adj文件指
定,最小内存阈值由/sys/ module/lowmemorykiller/parameters/ minfree文件指定,这两个文件在
itrc文件中有如下配置:
Memory management. Basic kerne parameters, and allow the high
level system server to be able to adust the kernel OOM driver
t parameters to match how it is managing things
write /proc/sys/vm/overcommit memory 1
write /proc/sys/vm/min free order shift 4
只有root和 system用户可以操作这两个文件
chown root system/sys/module/1owmemorykiller/parameters/adi
chmod 0664/sya/module/lowmemorykiller/parameters/adi
chown root system/sys/module/lowmemorykiller/parameters/minfree
chmod 664/ay/module/1owmemorykiller/parameters/minfree
on early-init


为init进程设置 OOM adi值为-16
write /proc/1/oom ad1 -16
start events
OOM adj I的取值范围是-17+15,值越大越容易被“杀死”。其中-17用于禁止进程在
发生OOM时被“杀死”。—16是init进程的 OOM adj 1值,这个值仅次于-17,但仍然可能被
杀死”,只是优先级最低
上述两个文件可以通过 ProcessList提供的接口予以修改。创建 ActivityManagerService
时,其成员变量 mProcessList初始化为 ProcessList类型的对象, ActivityManagerService f便可
以通过 mProcessList'管理系统的 OOM ad等级和每个等级的最小内存阈值。 ProcessList I的构
造函数如下:
Processhist0)(
//从/proe/ memento文件读取内存息,获取 MemTotal. MemFree. Cached的大小
MemInfoReader minfo new MemInfoReader(I:
minto. readMemInto (
/将 MemTotali转化为MF
mTotalMemMb minfo. getTotalsize()/(10241024)
updateoomLevels(0, 0, false) i
ProcessList在构造方法中调用了 updateOomLevels方法,代码如下:
class Processlist l
static final int PAGE SIZE -41024
// moomAd1数组中指定了6个 OOM adit等级,依次为10,1,2,49,15]
private final int[ moomAd] = new int[]
POREGROUND APP ADJ. VISIBLE APP ADJ. PERCEPTIBLE APP ADJ
BACKUP APP ADJ. HIDDEN APP MIN ADJ, HADDEN APP MAX ADJ
/*低端设备上对 mOraDi中定义的6种ad级别依次指定OOM最小内存阈值
低端设备通常指VGA或者分辨率更小的设备并且内存小于512MB的设备/
private final long [ moomMinFreeLow-new long[
8192,12288.16384/单位为KB
24576,28672,32768
/*高端设备上对 moomAdit中定义的6种adj依次指定0M最小内存阈值。高端
设备通常指分辨率为1280x800或更大,同时内存超过1G的设备*/
private final long[l moomMinFreeHigh new long[
32768,4096049152,//单位为KB
57344,65536.81920


StringBuilder asTring -new StringBuilder di
StringBuilder memstring-new StringBuilder (
比较内存和屏幕的比例系数,计算各个Mad等级对应的最小内存阈值
noat scale scaleMem scalebisp scaleMem: scalebisp:
iI (scale<0) scale -0i
else if (scale 1) scale a 1:
tor (int 1-0: long low mOomMinFreeLow(il
long high moomMinFreeHigh(1l:
/根据比例系数计算出实际的最小内存值
moomMinFreeli] -(long)(low+((high-low)scale))
f(10)4
ad]String. append(.)://不同 DOM ad]等级以,分割
memString. append(,")//不同最小内存值以“,“分割
ad]String. append( moraDi[i]):/ ooM adl等级
restring. append( mOomMinFreeli]*1024)/ PAGE SIZE)://最小内存阈值
f (write)//本例为a1se
writeFile ("/sys/module/lowmemorykiller/parameters/ads
astring tostring))
writeFile(/sys/module/lowmemorykiller/parameters/minfree"
memstring. tostring ()
//GB( Gingerbread):2048,3072,4096,6144,7168,8192
/HC( Honeycomb):8192,10240,1228,14336,16384,20480
updateOomLevels方法的主要工作是修改两个系统文件,进而更新系统的 OOM adj等级
和最小内存阈值。最终,/sys/ module/ lowmemorykiller/parameters/adj文件的内容如下:
0,1.2.4.9,15
sys/ module/lowmemorykiller/parameters/. minfree文件的内容与 AndroidI版本有关,假设其
内容如下:
2048,3072409661447168,8192//单位为page.默认1page4K
以上内容说明对于OOMa等级为0的进程,当系统内存低于2048×410248MB时


才会回收该等级的进程;对于 OOM adj等级为15的进程,当系统内存低于8192×4/1024
32MB时,就会回收该等级的进程。
12.3.2 LM Killer机制的实现
塑系
接下来分析 LM Killer I的实现,代码如下:
/定义 shrinker类型的1 owmem shrinker结构体
static struct shrinker lowmem shrinker -
shrink1 owmem shrink./ shrinker的回调函数
seeks DEFAULT SEEKS 16
static Int init lowmem ini:-(void)
register shrinker(slowmem shrinker) i
return 0:
static void exit lowmem exit(void)
unregister shrinker(slowmem shrinker
module init(1 owmem init)//模块初始化时,注册 shrinker方法
modu1 e exit( owmem exit//模块退出时,卸载 shrinker方法
在模块初始化时,通过 lowmem init方法调用 I register shrinker方法注册一个 shrinker类
型的 lowmem_ shrinker, shrinker与 Linux内存页回收有关,当需要回收内存页时,便会回调
shrink指定的方法,本例中要回调的方法为 lowmem shrink
lowmem shrinker在驱动层指定了 OOM ad等级和最小内存值,代码如下:
/oad等级
static int lowmem adi【6]-{//注意,数组大小为6
12,//只指定了4个值,其余为默认
//最小内存阈值
static int1 owmem minfree6】//数大小为6只指定了4个值其余为默认
3512
★6MB*
21024
BMB
41024
/*16MB*
161024
/★64MB*/


lowmem shrink函数被触发后的执行代码如下:
static int lowmem shrink(struct shrinker s, struct shrink control +se)
struct task struct * tsk:// Linux进程的表示形式
struct task struct selected NULL
int rem 0:


int tasksizei
int ii
int min score adi = OOM SCORE ADJ MAX +1
int selected tasks1ze-0;/被选择的进程,占用内存情况
义重
int selected oos score ad1//被选择进程的0Madj
1 nt array size ARRAY SIZE(1 owmem adi)// OOM ad]'等级的数量
int other free-g1 obal page state NR FREE PAGES);//空闲页数量
int other tile global page state(NR FILE PAGES)
g1oba1 page state(NR_ SHMEM)://缓存页数量
for [i 0i i array size: 1++)
//计算当前满足哪个最小内存闽值
if (other free lowmem minfree(i] gs other file< lowmem minfree[il)
//获取该内存闽值对应的 ooM ad
min score adi = lowmem adi【i]↓
break
rem= qlobal page state(NR ACTIVE ANON)+
global page state(NR ACTIVE FILE)
qlobal page state(NR INACTIVE ANON)
global page state(NR INACTIVE FILE)
//不要回收进程,说明此时剩余内存页大于64MB(目前最小内存值的最大值)
af (sc->n to scan <0 I man score ad m OOM SCORE ADJ MAX+ 1)
return rer
selected oom acore adl- min acore adla
rcu read lock()
tor each processtsk)//遍历进程
struct task struct . p
int oom score adi
//获取进程的 ooM ad值
oom score adi - p->signal->oom score ad1
if toom score adi min score adi)
task unlock(p)
continue


if (selected) t
/选择 OOM adl值较大的进程
if (oom score ad< selected oom score adi
continue:
// OM adi相同的情况下,选择使用内存更多的进程
1f (oom score ad1 m selected oom score adi 66
taskeize selected tasksize
continue


selected p:
selected tasksize-;
selected oom score adi= oom score adii
if (selected)
send sig(SIGKILL, selected,0):/发送 SIGKILL信号杀死”选择的进程
set tsk thread nag(selected, TIE MEMDIE) i
selected tasksize
return cendi
lowmem shrink选择进程“杀死”的三条原则是:
口 OOM adj i越大的进程越容易被“杀死”。
口相同 OOM adj的进程,占用内存越大的越容易被“杀死”。
口未达到最小内存阈值的最大值时,不会选择进程“杀死”。
至此 LM Killer就分析完了。


handle TrimMemory方法定义于 Activity Thread中,代码如下:
final void handleTrimMemory(int level)
final WindowManager Impl windowManager WindowManager Impl.getDetault()
通过 Window Manager回收 hardware surfaces和 resources,不在本文章中分析/
windowManager. startTrimMemory(level) i
//查询实现了 Componentcallbacks2接口的组件
ArrayList callbacks:
synchronized (pAckages)
callbacks collect ComponentCallbacksLocked(true, null) i
//回调各个组件的 onTrimMemory方法
final int N -callbacks size()
for (int i =0: 1< N: i++)
callbacks. get(i).onTrimMemory(level) i
windowManager.endTrimMemory()i
handle TrimMemory会循环回调各个组件的 on'TnimMemory方法,进行细粒度的内存整理
操作。但此操作会影响系统和应用程序的性能。
(2)利用 scheduleDestroyActivities方法销毁 Activity
schedule Destroy Activities方法位于 ActivityStack中,用于销毁 Activity,代码如下:
final class Activitystack
final void scheduleDestroyActivities(ProcessRecord owner
boolean somAdi, String reason) I
Message msg hAndler obtainMessage (DESTROY ACTIVITIES MSG)
msg.bi new ScheduleDestroyArgs(owner, oomAdi, reason)
mHandler.sendMessage (maq
scheduleDestroy Activities I向 ActivityStack的消息循环中发送 DESTROY_ ACTIVITIES
MSG消息。 Activity Stack消息处理器的代码如下:
final class Activitystack
final Handler mHandler. nev Handler()
public void handleMessage (Message msg)
switch (msg. what)
case DESTROY ACTIVITIES MSG:
ScheduleDestroyArgs args -(ScheduleDestroyArgs)msg obi
synchronized (mService)
destroyActivitiesLocked (args. owner, args moomAd, args.mReason)
运行 TOP APP、执行 Instrumentation、处理 Broadcast、执行 Service的进程称为前台进
程,其他进程称为后台进程。前台应用程序程的adj赋值为 FOREGROUND_APP_ADJ,后台
应用程序进程的adj赋值为 hidden Adj。其中前台进程和后台进程又根据 adj Type分为不同的子
类型,如上述代码所示。第二阶段只是对dj进行粗略划分,后续可能对其adj值予以调整。
3. computeOomAdjLocked()第三阶段
computeOomAdj Locked0第三阶段主要工作是细分和调整第二阶段划分的ad值,并从前
台进程和后台进程中细分出hien为fase的非隐藏进程。代码如下:
private final int computeoomAdiLocked(ProcessRecord app, int hiddenAdi
ProcessRecord TOP APP, boolean recurse, boolean doingAl1)
省略前两个阶段
boolean hasstoppingActivities false:
/在第二阶段,只有 TOP APP才会将 foregroundActivitiesl赋值为true
if ( foregroundActivities 6s activitiessize> 0)
tor (int 1-0: 1< activities5ize: 1++)
final ActivityRecord r a app. activities. get(i):
if(r. visible)//有可见但非前合的 Activity
if (adi> ProcessList, VISIBLE APP ADJ)
/将ad调整为 VISIBLE APP ADJ
ad ProcessList VISIBLE APP ADJ
app. ad]Type
visible"//调整为 visible
schedGroup -Process THREAD GROUP DEFAULT
app. hidden false;//有可见 Activity,对应进程非 hidden
toregroundActivities true;//调整为true
break:
else it (rstate-Activitystate. PAUSING I I
r, state
Activitystate, PAUSED)
/有处于 Pausing和 Paused状态的 Activity,调整其adj
if (ad ProcessList PERCEPTIBLE APP ADJ)
adi ProcessList PERCEPTIBLE APP ADJ
app. ad]Type -"pausing":
app, hidden false:
toregroundActivitles true:
else if (rstate = Activitystate STOPPING)
有 stopping状态的 Activity
app. hidden false


在第三阶段,满足以下条件的进程视为非隐藏进程:
口有可见但非前台的 Activity
口有可见 Activity,该进程有未被完全覆盖的 Activity,自然是非隐藏进程。
口有处于 Pausing和 Paused状态的 Activity.当 Activity处于 Pausing.状态时,其
onPause方法未执行或未执行完毕,依然可能是当前正在显示的 Activity。当 Activity
为 Paused.状态,即已经执行完 on Pause方法,虽然该 Activity不再显示,但其仍然
可能被用户感知,比如后台运行的音乐播放器。因此这两种情况均暂时视为非隐藏
进程
口有处于 STOPPING状态的 Activity。有在处于停止状态的 Activity,也暂时视为非
隐藏进程,因为该 Activity随时可能被 on Restart
口调用了AMS. setProcessForeground将进程强制切换到前台。调用 setProcessForeground
可以强制将一个进程切换到前台,因此视为非隐藏进程。
非隐藏进程并非只有以上几类,后续还会遇到。接下来分析 computeOomAdjLocked第四
阶段。
4. computeOomAdjLocked()第四阶段
computeOomAdjLocked0第四阶段只是调整四类特殊进程的ad值,这四类特殊进程也被
视为非隐藏进程。虽然为这四类特殊进程分配了特殊的adj值,但其adj值在后续阶段仍然可
能被修改。代码如下:


if (ad] ProcessListHoME APP ADJ ss app mHome Process)
/调整Home进程的ad为指定值
ad]= ProcessListHOME APP ADJ:
schedGroup Process THREAD GROUP BG NONINTERACTIVE
app. hidden false;//也是非隐藏进程
app. ad]Type -"home"
if (ad3 ProcessList PREVIOUS APP ADJ ss app - mPreviousProcess
ss app.activities.size()>0) 1
/调整上一个应用程序的adj
ad]- ProcessList. PREVIOUS APP ADJ:
schedGroup Process THREAD GROUP BG NONINTERACTIVE
app. hidden false;//非隐藏进程
app.ad]Type ="previous":
app. adi Seg mAdiSeg:
app. curRawAd] app. nonStoppingAdi -adi;
if (mBackupTarget null && app m mBackupTarget. app)(


口处理 BIND ABOVE CLIENT标记时,对当前进程adj降级处理。
代码如下:
private final int computeoomAd Locked(ProcessRecord app, int hiddenAdy
ProcessRecord TOP APP boolean recurse, boolean doingAlll
-省略前六个阶段
if (adi =m ProcessList SERVICE ADJ)
⊥( doing11)(//本例传入的参数为true
/ mNumSe ce Procs是上次计算ad时应用程序 Service的数量
mNewNumServiceProcs是本次计算adj时应用程序 service的数量
当 mNowNumServiceProcs大于 mNumServiceProcs的1/3时,将该
进程的ad调整为 SERVICE B ADJ/
app. service mNewNumServiceProcs> (mNumserviceProcs/3)
aNewNumServiceProcs卡
it (app. service)
adi ProcessList SERVICE B ADJ:
I else
app. service talse:
app, nonstoppingAdl-adj://无正在 Stopping的 Activity时,进程的ad
/进程有正在 stopping的 ctivity时,调整其adj为 PERCEPTIBLE APP ADJ
if (hasstoppingActivities)
if (adi ProcessLEst PERCEPTIBLE APP ADJ)
adi- ProcessList PERCEPTIBLE APP ADJ
app. adiType ="stopping":
app. curRawAdl-adj;//设置进程的当前初始adj
//计算出的adj不能大于进程的 maaDi,否则调整为 maxEd
⊥ad>app, maxEd// mandi创建 ProcessRecord时指定
ad] -app. maxEd]
11 (app.maxdi ProcessList PERCEPTIBLE APP ADJ)
schedGroup Process THREAD GROUP DEFAULT:
//adj小于 HIDDEN APP MIN ADJ视为保进程,不容易被“杀死
if (adl ProcessList HIDDEN APP MIN ADJ)
app. keeping truc:
)
*如果该进程通过 BIND APOVE CLIENT标记 bondservice到其他服务,说
明该进程的地位比 Service所在进程要低,需要将该进程的ad值适当
调高,当OM发生时,尽可能让该进程先于 Service所在进程被回收*/

/调整 backup进程的adj
if (adi ProcessList BACKUP APP ADJ) A
ad1- ProcessList BACKUP APP ADJ:
app. adiType " backup"
app. hidden- false;//非隐藏进程
调整四类特殊进程的时值后,开始执行 computeOomAdjLockedo第五阶段的工作。
5. compute OomAdjLocked0第五阶段
computeOomAdj Locked0第五阶段主要工作是,根据进程中拥有的应用程序 Service的启
动状态和绑定标记调整进程的adj值。代码如下
private final int computeoomAd]Locked(ProceasRecord app, int hiddenAdi
ProcessRecord TOP APP, boolean recurse, boolean doingAll)
-/略前四个阶段
//如果非前台进程,并且进程中包含 Service
1f (app. services.size()!=0 &6 (ad1> ProcessList FOREGROUND APP ADJ
II schedGroup m Process THREAD GROUP BG NONINTERACTIVE))(
final long now Systemclock.uptimeMillis(


至此1 BIND WAIVE PRIORITY0)分支结束
/如果客户堵绑定 Service时,设置了
BIND ADJUST WITH ACTIVITY绑定标记,则调整该进程
的ad值调整为 FOREGROUND APP ADJ或者保留当前值*/
//至此,for循环结東
//至此,内层h1e循环结柬
/至此,外层ifs. connections.size()>0----分支结束
/至此,外层wh1e循环结束
至此,计算出 Service对adj的影,如果计算结果大于 hiddenAdi,
要将其重新调整为 hiddenAdi,防止adj值过大导致该进程被杀死”
it (adi> hiddenAd1)
adi hiddenAdi i
app. hidden false;
app. adiType "bg-services"
这部分代码繁琐而低效,其核心思路是尽可能将该进程的adj值与使用该进程 Service的
Client进程的adj值保持同步。
6. compute OomAdjLocked0第六阶段
computeOom Adj Locked第六阶段主要工作是,根据使用该进程的 Content Provider的
Client的状态,调整进程的adj值。代码如下:
private final int computeDomAd]locked(ProceasRecord app, int hiddenAd]
ProcessRecord TOP APP, boolean recurse, boolean doinqAll) I
---省略前五个阶段
/如果非前台进程,并且进程中包含发布 Content Provider
if (app. pubProviders size():= 56(m,))
Iterator it - app. pubProviders values().lterator() i
while (it. hasNext ()6 (ad ProcessList FOREGROUND APP ADJ


这部分源码与第五阶段相似,但更简单,其核心思路是,尽可能将该进程的adj值与使
用该进程 Content Provider的 Client进程的adj值保持同步。
7. compute Locked0第七阶段
computeOomAdj Locked0第七阶段的主要工作如下:
口将一部分进程的adj由 SERVICE AD调整为 SERVICE B ADJ。
口将“无正在 Stopping的 Activity"时进程的adj值赋值为当前计算结果。
口调整“有正在 Stopping的 Activity”时进程的adj为 PERCEPTIBLE APP ADJ
口调整进程的当前初始adj为当前计算结果。
口将adj小于 HIDDEN APP MIN ADJ为保留进程,不容易被“杀死”。

computeOomAdjLocked0第八阶段的主要工作如下:
口根据adj值计算进程的 Importance值
口如果进程运行状态发生改变,需要将改变信息存入 ActivityManagerService mEnding
Process Changes成员变量中,然后向 ActivityManagerService的消息循环中发送
DISPATCH PROCESSES CHANGED消息,处理该消息时会回调 IProcessObserver的3
个方法: onForegroundActivities Change、 unimportance Changed、 onProcess Died
IProcessObserver是一个 Binder服务接口,目前还没有实现该接口的服务。代码如下:
private fnal int computeoomAdi Locked(ProcessRecord app, int hiddenAdl
ProcessRecord TOP APP, boolean recurse, boolean doinaAl1)

文章小结
文章分析了 Activity Manager进程管理的主要内容: LRU weight、 OOM adj、Low
Memory Killer详细分析了如何调整 LRU weight、如何计算 OOM adj以及 Low Memory Killer
的运行原理。进程管理不但会影响进程的生命周期,也影响应用程序组件的生命周期。同时
应用程序组件又根据自身的重要性影响着进程的生命周期。

  • 上一篇:今天讲讲安卓app开发中的activity manager进程管理
  • 下一篇:实战项目:上海App开发
  • © 2011-2018 www.keyrey.com 上海科睿网络科技有限公司 © 版权所有 沪ICP备12032097号-1
    友情链接 : 上海app开发 app开发公司 app制作 手机软件开发 手机软件开发公司 小程序开发 上海网站制作公司
    QQ在线咨询

    上海app开发QQ在线咨询 上海app开发QQ在线咨询
    电话咨询
    400-877-9280 app开发公司电话咨询
    即时在线咨询 手机软件开发即时在线咨询
    微信扫一扫
    添加app制作微信 上海网站制作公司微信
    科睿网络-互联网开发营销专家

    凡事有交代 件件有着落 事事有回应

    立即获取为您量身定制的开发营销方案

    咨询热线 400-877-9280