本文主要介绍Android的lowmemorykiller的oom_adj的相关概念,以及根据一些案例来阐述了解oom_adj对于做Android应用开发的重要意义。

一、lowmeorykiller中进程的分类以及各类进程的adj值

        在Android的lowmemroykiller机制中,会对于所有进程进行分类,对于每一类别的进程会有其oom_adj值的取值范围,oom_adj值越高则代表进程越不重要,在系统执行低杀操作时,会从oom_adj值越高的开始杀。系统lowmemeorykiller机制下对于进程的级别的以变量的形式定义在framework/base/core/java/com/android/server/am/ProcessList.java类中,可总结成下表:

再补充介绍一下:

1.AMS角度对于进程的分级       

上表带分级只是从lowmemroykiller角度来分的,时用于lowmemeorykiller执行杀进程操作,但是从android的系统管理角度看,即是从AMS执行相关逻辑时,又有一套自己的分级机制,当然这两套机制也有着很多互通的点。AMS角度的级别划分以变量的形式定义在framework/base/core/java/android/app/ActivityManager.java类中,以PROCESS_STATE开头的变量。

2.没有stopService其内含activity的后台进程

        这类进程从lowmemorykiller角度是划分为cached,因为如果这类进程往往占有较大的内存,这类含有activity的后台进程往往占有较大内存,所以即使这类进程包含了Service,lowmemorykiller的机制也会更加倾向于优先杀死这类进程。

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

        还有一点,这类进程虽然被lmk划分为cached进程,但是从ams角度是被划分为PROCESS_STATE_SERVICE这个类别的,即视为服务进程,在ams相关流程中也是以服务进程来执行相关逻辑的,此外在使用dumpsys meminfo查看所有进程时,这类进程也是被列在B service这个类别的。

3.A-Service与B-Service的划分

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

4.perceptible的标准

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

二、如何查询应用的adj级别

1.dumpsys meminfo

        使用dumpsys meminfo命令时,会列出当前系统的所有进程,不同进程放入不同的分类,对应的分类名基本与lmk的分类一致。有一点不同的就是,退到后台启动了服务且显示过UI的进程,在dumpsys meminfo命令中会归为b service一类,但从lmk角度分配的oom_adj值为9~16的范围,属于cached一类

2.cat /proc/[PID]/oom_adj:  使用该命令会直接显示出对应进程号的adj值

三、未控制好oom_adj的案例

1.ui进程启动service的隐患

案例a:备份进程启动一个服务开始执行备份,备份服务运行在ui进程(服务未调用startForeground())

隐患:备份服务一般需要较长时间,在用户按Home键退出后台后,备份进程会处于previous状态,继续使用手机其他应用,会是使得备份进程处于cch-started-ui-services的状态,即是启动了服务并且包含ui的进程退到后台状态,此时进程的adj值处于9~16,随着时间推移逐渐增大。如果在较长的备份过程中,触发了lowmemorykiller,很容易导致备份进程被杀掉,从而导致备份的失败。

案例b:备份进程启动一个服务开始执行备份,备份服务运行在ui进程(服务调用了startForeground())

隐患:这种情况下备份进程会被划分为perceptible进程,基本上是不会被lowmemorykiller杀掉的,但是这也导致内存占用较大的备份常驻了,从内存管理角度来将,备份进程的UI部分是并不期望他常驻的,而大量内存的常驻也容易导致lowmemorykiller的出现,从而导致系统进入内存较低的等级,而当系统处于内存较低等级时,会触发系统回调所有进程进行进程回收动作,容易导致系统卡顿场景的出现。此外,调用了startForeground()会导致进程被系统判定为前景进程,这样备份进程便会抢占用户操作手机时前台应用的cpu资源,增加了卡顿场景出现的几率。

解决方法:将Service运行在独立的进程,这样应用退到后台后,备份服务进程会处于A-Service中(逐渐掉落到B-Service),而B-Service进程一般也是很难被lowmemorykiller砍。该独立是否要startForeground()?如果期望保证备份尽快到完成,便可以牺牲一些用户在操作其他应用时到用户体验,将服务推为前景应用;对于很多需要保证功能的流畅运行的服务进程,例如音乐播放,录音等,则需要将这类服务进程通过startForeground()设置为前景进程,但前提还是需要做到ui与Service分离。

 

2.使用线程解决耗时操作造成anr问题的隐患

案例:短信、邮件、或笔记本应用,在用户按BACK键时存下草稿

public class MyActivity extents Activity {
public void onPause(){
//存储草稿
}
}

问题(1):由于存储草稿定操作一般时保存到数据库,某些情况下可能会占用较长时间,这里就有可能导致anr的隐患

解决方案1:

public class MyActivity extents Activity {
public void onPause(){
new Thread() {
public void run() {
//存储草稿
}
}.start()
}
}

问题(2):当用户以back键离开应用时(以home键离开会处于previous状态),应用退到后台处于empty-cached状态,内存不足时,可能会立刻杀。

解决方案2:如果线程有这问题,是否可以用服务来完成存储草稿的动作呢?

问题(3):如果用服务来存储草稿,即将存储草稿动作写在onStartCommand中,由于onStartCommmand操作依旧是执行在主线程的,所以在其中执行耗时操作时,依旧可能会导致ANR

最终解决方案:使用IntentService来执行保存草稿的动作

public class MyActivity extents Activity {
public void onPause(){
...
startService(new Intent(this, MyIntentService.class));
...
}
} public class MyIntentService extends IntentService {
protected void onHandleIntent(Intent intent) {
//保存草稿
}
}

3.provider被binder导致的隐患

案例:systemui进程获取手机中的手机管家应用提供的content provider,用于获取当前应用相关信息

问题:管家应用的UID为System,在Android机制中,System UID进程提供的provider一旦被访问,即使访问完成关掉provider后,连接依旧存在,所有管家应用由于其provider持续被persistent进程咬住,所以管家应用便会长时间处于foreground级别的应用中,oom_adj为0,导致管家应用占用的大量内存很难被回收

解决方案:单独使用一个进程提供provider,提供provider进程由于占用内存较小,所以即使无法被回收也影响较小,这样管家应用的UI进程能够按照系统正常的回收流程在需要时被回收

四、总结一些经验

1.进程在启动服务后,在事情做完后,必须呼叫stopService或stopSelf通知框架,避免事情做完了,服务进程依旧常驻内存

2.对于需要长时间停留在后台的服务,且服务设置为具有重启特性时,需要做到ui与service分离,一避免占用内存较大的ui进程的常驻

3.对于需要长时间停留在后台的服务,且服务设置为具有重启特性时,不可长时间bind住其他进程的service或provider,避免其他进程常驻;

4.常驻性质的进程(oom_adj<=6),不可一直bind住其他进程的服务或provider,用完必须马上放掉

5.不可使用SystemUID进程内的provider,在Android设计中,若针对System UID的进程使用provider,即使已关掉provider,但框架仍会保持provider connection

6.利用onStartCommand方法回传值(START_STICKY,START_NOT_STICKY等)控制好服务的重启属性,在设计时充分考虑进程被lmk杀死的情况

7.IntentService继承自Service,针对CPU scheduling、工作排程等都有完整实现,建议多采用IntentnService进行功能实现

Android内存管理篇 - adj的概念与进程adj级别控制的更多相关文章

  1. Android内存管理篇 - 从updateOomAdjLocked看lowmemorykiller之外的Android进程回收机制

    提起android的进程回收机制,大家所熟知的是Android的lowmemroykiller的机制.当系统可用内存低于某个阀值时,即会杀死这个阀值对应的Adj值的所有应用.但是本篇文章并为是要介绍L ...

  2. Android - 内存泄漏 + 垃圾回收(GC)概念

    Android内存泄露——全解析和处理办法 内存泄露 说到内存泄露,就不得不提到内存溢出,这两个比较容易混淆的概念,我们来分析一下. 内存泄露:程序在向系统申请分配内存空间后(new),在使用完毕后未 ...

  3. Android内存进程管理机制

    参考文章: http://www.apkbus.com/android-104940-1-1.htmlhttp://blog.sina.com.cn/s/blog_3e3fcadd0100yjo2.h ...

  4. 正确认识Android的内存管理机制,合理关闭进程 (一)

    随着大家收货后会有很多乐粉晒内存,为啦方便大家,在网上搜集了一些相关Andriod管理的相关机制合理管理内存,整理下发个贴. 首先要知道Android系统是基于Linux 2.6内核开发的开源操作系统 ...

  5. Android系统开发(8)——linx进程基本概念

    一.proc文件系统 传统意义上的文件系统是用于块设备上信息的存储,/proc这个目录是一个虚拟文件系统,它放置的数据都是在内存当中,所以这个目录本身不占用任何硬盘空间.主要包含如下系统信息: 内存管 ...

  6. Android内存分析命令(转)

    一.概述 1.1 内存指标概念 Item 全称 含义 等价 USS Unique Set Size 物理内存 进程独占的内存 PSS Proportional Set Size 物理内存 PSS= U ...

  7. Android 内存管理 &Memory Leak & OOM 分析

    转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...

  8. android 内存优化以及性能优化相关问题

    最近做一个android 的应用程序 总是出现内存高 和cpu高的问题困扰了好多天. 下面为自己从网上总结的和自己找到的问题. 1. WebView  控件: 使用了 WebView 控件一定要注意清 ...

  9. Android 内存管理分析(四)

    尊重原创作者,转载请注明出处: http://blog.csdn.net/gemmem/article/details/8920039 最近在网上看了不少Android内存管理方面的博文,但是文章大多 ...

随机推荐

  1. Mac系统下 解决ThinkPHP生成目录,无法保存问题

    Mac环境下我们建立目录的时候往往要增加目录的时候要修改权限,输入密码,大大的降低了效率. 解决办法: 1.找到你的目录站点 终端打开打 2.终端输入find file -exec sudo chmo ...

  2. Cookie保存中文用户名报错(500)

    在用Cookie保存用户名时候,当用户名是中文的时候服务器报错了. HTTP Status 500 - An exception occurred processing JSP page /dolog ...

  3. 语音端点检测(Voice Activity Detection,VAD)

    本文内容均翻译自这篇博文:(该博主的相关文章都比较好,感兴趣的可以自行学习) Voice Activity Detection(VAD) Tutorial 语音端点检测一般用于鉴别音频信号当中的语音出 ...

  4. 死锁问题------------------------INSERT ... ON DUPLICATE KEY UPDATE*(转)

    前言    我们在实际业务场景中,经常会有一个这样的需求,插入某条记录,如果已经存在了则更新它如果更新日期或者某些列上的累加操作等,我们肯定会想到使用INSERT ... ON DUPLICATE K ...

  5. MySQL PROFILE 跟踪语句各阶段性能开销

    PROFILE  可以跟踪查询语句各个阶段 Time,IO,CPU,MEMORY 等资源使用情况,比较详细.所以系统一般不会记录太多.启用是全局的,所以每个连接都保持语句的资源使用情况. 查看 PRO ...

  6. 无法获得锁 /var/lib/dpkg/lock - open

    关于 apt-get update 更新的时候出现 无法获得锁 /var/lib/dpkg/lock - open 解决 直接删除这个锁文件即可: sudo rm /var/lib/dpkg/lock ...

  7. 第三次OO总结

    规格化设计的调研 随着50年代高级语言的出现,编译技术不断完善,涌现出多种流派的语言,其中就有里程碑式的Pascal语言:进入70年代,由于众多语言造成的不可移植.难于维护,Ada程序设计语言诞生了, ...

  8. ionic 侧栏菜单用法

    第一步: 引入js和css文件我这里是直接引入的cdn,ionic是基于angular的,bundle.min.js把常用angular的js已经压缩到一起,可以直接引入.bundle.min.js, ...

  9. 安装MySQL5.7.19 网上的文章参考 并做了部分修改

    1,从官方网址下载MySQL5.7.19源码包 http://dev.MySQL.com/downloads/mysql/#downloads     2,安装好CentOS6.7 64位操作系统. ...

  10. Python3.* 和Python2.*的区别

    许多Python初学者都会问:我应该学习哪个版本的Python.对于这个问题,我的回答通常是“先选择一个最适合你的Python教程,教程中使用哪个版本的Python,你就用那个版本.等学得差不多了,再 ...