*本文主要介绍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类中,可总结成下表:*

[外链图片转存中…(img-Ad1ffcK0-1612332903000)]

*再补充介绍一下:*

*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进行功能实现*

Activity管理(二):adj内存管理机制相关推荐

  1. 1709 ltsb 内存占用_「正点原子STM32Mini板资料连载」第三十二章 内存管理实验

    1)实验平台:正点原子STM32mini开发板 2)摘自<正点原子STM32 不完全手册(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第三十二章 内存管理实验 上一章,我 ...

  2. 属性与内存管理(属性与内存管理都是相互关联的)

    <span style="font-size:18px;"> 属性与内存管理(属性与内存管理都是相互关联的)第一部分一,属性:属性是OC2.0之后出来的新语法,用来取代 ...

  3. C++:内存管理:C++内存管理详解

    C++语言内存管理是指:对系统的分配.创建.使用这一系列操作.在内存管理中,由于是操作系统内存,使用不当会造成很麻烦的后果.本文将从系统内存的分配.创建出发,并且结合例子来说明内存管理不当会造成的结果 ...

  4. linux系统内存管理含义,Linux内存管理--基本概念及相关数据结构

    一.内存管理的基本概念 1.存储空间 在32位嵌入式系统中,存储空间的地址范围从0x00000000到0xFFFFFFFF.这4GB存储范围内可以包括以下几种存储空间: 设备空间(MT_DEVICE) ...

  5. 红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理)丨epoll丨c/c++linux服务器开发丨linux后台开发

    红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理) 视频讲解如下: 红黑树在linux内核中的应用场景(红黑树,进程管理CFS,内存管理)丨epoll丨c/c++linux服务器开 ...

  6. Block介绍(二)内存管理与其他特性

    为什么80%的码农都做不了架构师?>>>    我们在前一章介绍了block的用法,而正确使用block必须要求正确理解block的内存管理问题. 这一章,我们只陈述结果而不追寻原因 ...

  7. linux内存管理简介,Linux内存管理机制简介

    在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,区别于 Windows的内存管理. 主要特点是,无论物理内存有 ...

  8. OS之内存管理 ---基本的内存管理策略(二)

    分段 基本方法 分段就是基于用户视图的内存管理方案.逻辑地址空间是由一组段构成的,每个段都有名称和长度.地址指定了段名称和段内偏移.因此用户通过两个量来指定地址:段名称和段偏移. 为了简单,进行对段的 ...

  9. 《Linux内核设计与实现》读书笔记(十二)- 内存管理

    内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己来解决(用户空间的内存错误可以抛给内核来解决). 所有内核的内存管理必须要简洁而且高效. 主要内容: 内存的管理单元 获取内存的方法 ...

最新文章

  1. java中磁盘和内存的相互,Java:有没有磁盘和内存一样快的情况?
  2. asp.net treeview控件无刷新选择和删除节点的ajax方法
  3. PHP 5.4 的新特性
  4. E2. 比昨天更多的棒棒糖 (Hard)
  5. DL之FastR-CNN:Fast R-CNN算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  6. 面向对象的程序设计在游戏开发中使用(一):类
  7. hdoop(2)——hdfs一些常用的配置文件
  8. 第六节:教你如何在html中绑定数据
  9. Java中isAssignableFrom()方法与instanceof()方法用法
  10. Puppet安装部署篇(一)
  11. Vue源码学习(三)——数据双向绑定
  12. 第四季-专题17-LCD驱动程序设计
  13. 为IT部门画一个“饼”
  14. Python代码加密方案总结(巨全面和详细)
  15. 富文本编辑器 可全屏可粘贴(只能单独粘贴图片或文字)
  16. [原创]火箭发动机设计---民科版
  17. Spring Cloud 基本理论概述
  18. CSS去除input框自带的叉号
  19. 专利第三方评估_什么是第三方风险评估,您该如何做?
  20. 记录nodejs使用express搭建一个api服务器程序(5)-nodejs操作SQL数据库,Sequelize和Knex

热门文章

  1. 服务器硬件电路设计书籍,家庭网关硬件接口电路设计大全——电路精选(3)...
  2. 修改class文件_VM实战(六) - 通过案例深入学习class文件结构原理
  3. c++ocx交互检测弹框_吉利几何C:2022杭州亚运会移动“明信片”!
  4. matlab柱状斜线_Matlab:柱状图饼状图填充不同条纹
  5. 2.MOC文件解读(上)——MOC文件中的数据
  6. OPENCV图像创建,保存和复制
  7. MFC指定位置画位图
  8. Django restfulframework 开发相关知识 整理
  9. 文件服务器vsftp的配置
  10. 十字消源码分享(基于libgdx开发)