【朝花夕拾】Android性能篇之(六)Android进程管理机制

  1. 云栖社区>
  2. 博客>
  3. 正文

【朝花夕拾】Android性能篇之(六)Android进程管理机制

宋者为王 2018-06-11 17:21:00 浏览109
展开阅读全文

前言

       原文:【朝花夕拾】Android性能篇之(六)Android进程管理机制

       Android系统与其他操作系统有个很不一样的地方,就是其他操作系统尽可能移除不再活动的进程,从而尽可能保证多的内存空间,而Android系统却是反其道而行之,尽可能保留进程。Android这样设计有什么优势呢?又是通过怎样的方法来管理这些被保留的进程的呢?Android用户又该如何正确使用手机从而更好发挥Android系统所特有的优势呢?本文将一一为您解开这些谜团。

      本文的主要内容如下:

 

          

 

一、Android进程管理的特殊设计

       Linux系统对进程的管理方式是一旦进程活动停止,系统就会结束该进程。尽管Android基于Linux Kernel,但在进程管理上,却采取了另外一种独特的设计:当进程活动停止时,系统并不会立刻结束它,而是会尽可能地将该进程保存在内存中,在以后的某个时间,一旦需要该进程,系统就会立即打开它,而不用再做一些初始化操作。只有当剩余内存不够用了,为了维持新开启的进程或者比较重要的进程的正常运行,系统才会选择性地杀掉一些不重要的内存,腾出内存空间来,所以Android系统永远不会有内存不足的提示。

 

二、Android独特进程管理设计的好处

      Android这种独特的设计,也正是Android标榜的优势之一,这有两个好处:

  1、最大限度地提高内存的使用率。

       比如,你的内存是8G,如果每次使用完某个进程就杀掉,那么被使用的内存基本上会始终保持在某个值,比如4G以内,那么内存的使用率就总是保存在50%以内,剩余的4G内存形同虚设,发挥用处的机会非常少。而Android的这种设计,就可以做到有多少内存就用多少内存,尽可能大地提高内存使用率。同样比如有8G内存,使用完的进程仍保留在内存中,累积下来,被使用的内存就尽可能地会接近8G。

  2、提高再次启动时的启动速度

       被驻留在内存中不再活动的进程(后台进程或空进程,后面会再讲到),很多是经常需要使用的,当再次使用该进程的时候,系统立即打开它,而不需要再重新初始化。例如,我们常用的浏览器,当暂时不再使用时,按下Home键或Back键,浏览器进程就变成了不再活动的进程。如果下次又要使用了,点击多任务键,在最近使用应用列表中点击浏览器即可,浏览器界面仍然保持着退出前的界面。但如果退出时把该进程移除了,那么再次使用时,就需要重新初始化,然后进入该应用,这往往会花费不少的时间。

 

三、Android进程的五个等级

        Android系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,以此类推,以回收系统资源。该“重要性层级结构”将进程分为了五个等级:

  1、前台进程(foreground)

       前台进程是指那些有组件正和用户进行交互的应用程序的进程,也称为Active进程。这些都是Android尝试通过回收其他应用程序来使其保持相应的进程。这些进程的数量非常少,只有等到最后关头才会终止这些进程,是用户最不希望终止的进程。例如:而当你运行浏览器这类应用时,它们的界面就会显示在前台,它们就属于前台进程,当你按home键回到主界面,他们就变成了后台程序。

       如果一个进程满足以下任一条件,即视为前台进程:

     (1)托管处于活动状态的Activity,也就是说,它们位于前台并对用户事件进行响应,此时的情形为响应了Activity中的onResume()生命周期方法,但没有响应onPause()。

     (2)托管正在执行onReceive()方法处理事件程序的BroadcastReceiver。

     (3)托管正在执行onStart()、onCreate()或onDestroy()事件处理程序的Service。

     (4)托管正在运行且被标记为在前台运行的Service,即调用了该Service的startForeground()方法。

     (5)托管某个Service,且该Service正绑定在用户正在交互的Activity的Service,即该Activity正处于活动状态。

  2、可见进程(visible)

        没有任何前台组件、但仍然会影响用户在屏幕上所见内容的进程。如果一个进程满足以下任一条件,即视为可见进程:

    (1)托管不在前台、但仍对用户可见的Activity(已调用其onPause()方法)。例如:如果前台Acitivty启动了一个对话框,或者启动了一个非全屏,亦或是一个透明的Activity,允许在其后显示上一个Activity,则可能会发生这种情况,这类Activity不在前台运行,也不能对用户事件作出反应。

    (2)托管绑定到可见Activity的Service。(官网上说是绑定到可见或前台Activity,但笔者有一点疑问,这个和“前台进程”中第(5)点相矛盾吗,绑定到前台Activity,那就是前台进程了)

        可见进程被视为是极其重要的进程,这类进程的数量也很少,只有在资源极度匮乏的环境下,为保证前台进程继续执行时才会终止。

  3、服务进程(Service)

        正在运行已使用startService()方法启动的Serice且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

       有些资料上面也称这种进程为次要服务(Secondary Service),而属于上述两个更高类别的进程则被称为主要服务,主要服务往往属于系统进程,如拨号进程等,不可能被进程管理轻易终止。这里我们以Android开发者官网的称呼为标准,称为服务进程。

  4、后台进程(hidden)

       包含目前对用户不可见的Activity,即该Activity调用了onStop()方法。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供上述三个更高级别的进程使用。通常会有很多后台进程在运行,它们会保存在LRU(Least Recently Used,最近最少使用)列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该Activity时,Activity会恢复其所有可见状态。

       这里读者可以做个试验,先开启微信,进入到朋友圈界面, 然后点击手机屏幕下方的导航栏中的Home按键进入到后台,再点击最近使用应用列表显示按钮(不同的手机位置不一样,有的在Home键左边,有的则在Home键右边),在显示的最近使用应用的列表中清理掉微信应用,最后再点击桌面的微信图标启动微信,会发现显示的界面仍然是朋友圈界面。

       后台进程,我们可以简单理解为,应用(只考虑只有Activity组件的情况)启动后按Home键后被切换到后台的进程。如浏览器、阅读器等,当程序显示在屏幕上时,它们所运行的进程即为前台进程(foreground),一旦按home键(注意不是back键)返回到桌面,程序就停留在后台,成为后台进程。

  5、空进程(empty)

       不含任何活动应用组件的进程。保留这种进程的唯一目的是用作缓存,以缩短下次再其中运行组件所需要的启动时间。一般来说,当应用按back按键退出后应用后,就变成了一个空进程。比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。当系统内存不够用时,无疑,该进程是应该最先终止的。在最近使用应用列表中,可以看到按back键退出的应用。

       根据进程中当前活动组件的重要程度,Android会将进程评定为它可能达到的最高级别。通俗地说,就是如果一个进程同时拥有多个对应上述不同等级进程的组件时,会以最高的那个等级作为该进程的等级。例如,如果某进程托管着服务和可见Activity,则会将此进程评定为可见进程,而不是服务进程。

       此外,一个进程的级别可能会因为其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。例如,如果进程A中的内容提供程序为进程B中的客户端提供服务,或者如果进程A中的服务绑定到进程B中的组件,则进程A始终被视为至少与进程B同样重要。

       由于运行服务的进程其级别高于托管后台Activity的进程,因此启动长时间运行操作的Activity最好为该操作启动Service,而不是简单地创建工作线程,当操作有可能比Activity更加持久时更应该如此。例如,正在将图片上传到网站的Activity应该启动服务来执行上传,这样一来,即使用户退出Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论Activity发生什么情况,该操作至少具备“服务进程”优先级。如果某个Activity开启了线程执行耗时操作,当Activity退出时,该Activity的实例将不会释放内存资源,直到线程执行完,这样容易导致内存泄漏。同理,广播接收器也应该使用服务,而不是简单地将耗时冗长的操作放入线程中。

       

四、进程移除顺序的依据——阈(yu,第四声)值

        前面讲到,内存不够用时,会根据进程的等级来决定优先回收哪类进程。那么系统是根据什么来判断需要移除这些进程的时机的呢?答案是阈值。

  1、查看阈值

        我们可以采用如下方法查看手机中各个等级进程的阈值(需要root权限),如第二排数据所示(其单位为页):

        

       以第一个数据44032为例,计算方法为:

       1page=4KB=4*1024B=4096B

       44032page* 4048B/page =  180355072B

       180355072B/1024/1024 = 172M

       即第一个等级的进程的阈值为172M。依次类推,阈值依次为:172M,190M,208M,226M,316M,415M。

       有必要说明一下,在Android开发者官方文档中,是将Android应用进程分为了5个等级,但很多资料却是分的6个等级,在后台进程和空进程之间还有一个“内容提供节点(content provider)进程”。内容提供节点,没有实体程序,仅提供内容供别的程序去用 ,比如日历供应节点,邮件供应节点等,在终止进程时,这类进程有比较高的优先权。手机中应该是采用的6个等级的方式,如上六个数据,正好对应着六个等级的进程,等级越高,阈值越低,即前台进程阈值为172M,空进程为415M。当系统的剩余内存只剩余不到415M的时候,系统首先会回收空进程,依次类推,只有剩余内存不到172M了,才会去回收前台进程,这样就起到了优化保护重要进程的作用。

 

五、Home键、Back键和多任务键

       Home键、Back键和多任务键,在手机屏幕的下方,这三个按键一般称为导航栏,中间的按钮为Home键,多任务键和Back键分别在其左右,一般根据手机品牌不同,左右位置也有所差异。

       在运行App的时候,如果按一下Home键或者Back键,都可以退到桌面,那么这两者有什么区别呢?

  • Home键。按Home键的时候,App如果没有Service开启,会从一个前台进程转变为一个后台进程;如果有前台service运行,就仍然是前台进程,比如QQ音乐播放器等;如果是只有普通service运行,那么就转变为服务进程(参照前文中讲的Android进程的5个级别)。
  • Back键。按Back键的时候,App如果没有Service开启,会从一个前台进程转变为一个空进程;对于有Service运行的情况,和按Home键一样。

        后台进程和空进程,都是驻留在后台,处于暂停状态,也都是除了占用一部分内存外,不占用其他如cpu等资源的,那么问题来了,为什么要设计后台进程和空进程这两种空进程呢?它们的区别到底在哪里呢?我们在前文讲Android进程的5个等级的时候讲到过,当剩余内存不足的时候,系统会按照等级顺序,优先移除不太重要进程,以收回内存供更重要的进程运行。那么,它们的区别就是,在剩余内存不足时,会优先移除空进程,再不足,才会移除空进程。所以,如果确实要退出某个应用一段时间内不大使用了,如果这款应用有退出按钮,就用应用自带的退出功能;如果没有,则最好按系统的Back键,这样可以变成空进程,当系统要回收内存时,就会优先被回收,从而释放的所占的资源。如果只是暂时退出去做点别的,过一会还要切换回来,或者对这款应用使用比较频繁,那就使用Home键,因为相比于按Back键,这样可以尽可能保住后台进程,方便下次使用的时候快速启动。

       当然,按Home键或Back键,对用户来说,其实感觉不到差异,使用起来没什么两样,但是,对于Android开发者来说,却有必要作为常识来了解其中的道理和差异。无论是按Home键还是按Back键,在按多任务键的时候,都可以看到这些进程,如下图所示。最下面的按键为清理按键,点击后可以清除掉这些进程,回收内存了,当然,前面也讲了很多遍了,不建议这样做。

       

 

  2、修改阈值。

       可以采用命令:echo "44032,48640,53248,57856,80896,106241" > /sys/module/lowmemorykiller/parameters/minfree来修改阈值,如下所示:

       

       重启后,会恢复为原来的值。至于如何永久性修改该阈值,这里不深入探讨,有兴趣的童鞋可以自行研究,一般来说,就按照系统给定的默认值使用就可以了,没特殊用途的话,没必要修改。

       对于这一节阈值的内容,暂时先讲到这里,如果要更深入,可以自行多研究研究。笔者也没有看到比较好的更深入的文章,所以也不好推荐,如果读者看到比较好的,可以推荐给笔者,感激不尽。

 

六、开发者选项中的进程管理功能

        Android手机都带有开发者选项,隐藏了很多功能,顾名思义,这些功能主要用于辅助开发者调试程序用的。其中有一些就是关于进程管理功能的,笔者这里简单介绍一下其中两款,如下图红框部分所示:

       

  • 不保留活动。用户离开以后即销毁每个活动(Activity),这样做使得后台进程都被销毁了。笔者试验过几款app,比如微信,浏览器,开启/关闭“不保留活动”前后,按Home键后,再打开应用,有明显的差别。当然,也试用了短信,DD打车,就没看出起了什么作用。读者若是感兴趣可以深入研究研究,到时候在指导指导笔者!
  • 后台进程限制。如下图所示,给出了后台进程个数限制的选项。

       

 

七、进程管理软件的使用

       Windows操作系统用户往往总想着保留更多的内存,在使用Android手机的时候,喜欢经常清理后台进程或空进程,而且清理完后,心里有一种特别爽的感觉,就像给家里做了一次大扫除一样,笔者最初使用Android手机的时候也是这样的心态-_-!基于这样的心态,一些进程清理软件,很受普通用户的青睐。其实这样做却正好抹杀了Android系统所标榜的优势,如前文所讲到的。

       那么进程管理软件有无必要呢?当然有的,只是需要注意使用场合。当需要运行大型程序的时候,可以手动关闭掉一些进程,腾出足够的空间供大型程序使用,这样就可以有效避免系统调用进程调度策略而引起的卡顿,这一点,第八大点第3小节中会有说明。而且由于开发者的原因,可能是程序写得太烂,或程序容易出错,或做不该做的动作,或是恶意程序,对于这类程序进程,手动移除也是有好处的。

       但如果是运行一些小程序,就完全没有必要去预先杀进程了,完全可以交给系统自己管理。读者可能会疑惑,因为小程序启动的时候,也有可能会因为内存不足而导致需要移除部分进程的情况。笔者认为,即便是内存不足,小程序运行引起的调用进程调度策略测的次数非常少,要移除的进程也非常少,产生的影响不大。同时,我们也要意识到另外一点就是,无论是手动杀死进程还是自动杀进程,都需要cpu去执行这些任务,所以也会拖慢手机和消耗电量。所以从这一点看,频繁杀进程,也是一个不好的习惯。

 

八、答疑解惑

       在以前没有专门去了解Android进程管理机制的时候,甚至是在研究的过程中,笔者心里都经常存在很多疑惑,以下整理了其中5个,不知道读者您是否有也类似的困惑呢?

  1、这么多驻留在内存的进程,不会耗电吗?

       大多数用惯了Windows操作系统的童鞋,看到Android系统尽可能保留不在活动的进程的设计,可能第一反应就是质疑,难道这样不会增加耗电量吗?其实,但一个程序按home键变成后台进程或者按back键退出变成空进程后,其实已经被暂停了,只保留了运行状态,不会消耗cpu,一个程序会耗电,是因为它需要调用cpu来运算,现在不消耗cpu了,当然就不会耗电了。当然,开了service的应用就另当别论了,比如QQ音乐播放器,当按home键或back键后,音乐仍然播放,是因为它开启了服务,而且是一个前台服务,在后面我们会继续讲到,此时它是一个前台进程,而不是后台进程或空进程。

  2、为什么一个不太app,运行时会占用很大的内存呢?

        我们经常会碰到这样一种现象,一个只有20M的App,运行起来的时候,却会耗掉100M以上的内存。一方面是,程序运行时为对象分配内存,另一方面,是Android虚拟机的原因。Android中的应用启动的时候,系统都会给它开启一个独立的虚拟机,这样做的好处是可以避免虚拟机崩溃导致整个系统崩溃(此处可以参考笔者以前的文章:【朝花夕拾】Android性能篇之(五)Android虚拟机),代价就是耗用更多的内存。

  3、为什么内存少的时候,运行大型程序会卡顿呢?

        当剩余内存不多时,打开大型程序,系统会触发自身的进程调度策略,去移除一些等级比较低的进程来回收内存,以供大型程序运行。而这个进程调度策略在决定哪些进程需要被移除的过程,是一个十分消耗资源的操作,特别是一个程序频繁像系统申内存的时候,这样就导致了系统的卡顿。

 4、应用开得太多了,手机变慢,是因为内存被占用太多吗?

        其实手机变慢的根本原因是cpu被耗用太多,而不是内存占用太多,因为真正执行程序所要完成的任务的最终执行者是CPU,而不是内存(RAM)。在内存足够的情况下,如果系统中占用cpu的进程太多,那无疑cpu总有忙不过来的时候,那肯定就会变慢了。这就好比,在一条道路上驾车,道路就像内存,车的引擎就像cpu,如果车的引擎的动力不够,或者承载的货物太多,车都跑不快,即便是道路上一路畅通无阻,也无济于事。所以,内存占用多少并不重要,只要道路提供给车辆前行的空间是足够的,手机变慢的责任,就和内存无关了。这个比喻用来解释第三点也很恰当,道路提供的车辆前进的空间无法满足车辆所必需的空间时,就需要交通机制花时间来调节交通,给这辆车提供足够的空间,而在此期间,这辆车只能乖乖候着。

  5、Android手机越用越慢,是什么原因呢?

       Android手机常常是越用越慢,即使是恢复出厂设置,也无法改变这个现象。手机越用越慢,主要由如下几个原因:(1)虚拟机机制问题。这一点在上一个问题中也提到了,在Android4.4以前的系统,使用的是Dalvik虚拟机,它的设计机制有缺陷,就是越用越慢;在Android4.4系统中有切换按钮,可以在Art虚拟机和Dalvik虚拟机之间切换;在Android4.4以后的系统就彻底抛弃了Dalvik而全面使用Art(读者可以参考:【朝花夕拾】Android性能篇之(五)Android虚拟机)。(2)开启了太多的服务,导致耗用太多的CPU。随着手机开机使用时间的增长,应用使用越来越多,很多应用看似退出了,而其实后台可能开了不少的服务,而他们可能还没有关闭。这些服务正在执行一些操作,会消耗CPU,而CPU才是手机变慢的根本原因。 而且Android app比较开放的,有很多不良应用充斥其中,可能对服务处理不当,滥用服务等,增加系统中的服务。(3)系统频繁调用自身的进程调度算法。这一点在前面已经说明了,这里不再赘述。(4)手机硬件的自然老化。这一点请阅读:手机越用越慢?鲁大师老化实验室告诉你真相 。

 

       本文的内容主要来源于Anroid官方开发网站、各种技术博客、以及自己的实际操作总结,在整理这方面知识的过程中,也渐渐解开了不少曾经的疑惑,对Android系统有了更多的认识。本文中笔者在不断强调,不要经常性地杀进程,主要是广大的朋友们受Windows操作系统的毒害太深,一时难以改变旧有的观念,所以不得不啰啰嗦嗦一再提起,以图加强读者的认识。受限于文章篇幅和笔者的知识,本文没有更深入地从源码角度去分析,更多地是在普及Android进程管理机制方面的基础知识。如果文章中有不当或不正确的地方,请读者不吝赐教,也更希望这篇文章能帮助更多的人提高多Android的认识,谢谢!

 

推荐阅读

       Android开发者官方指导网站

       Android内存管理机制

       Android内存管理篇-adj的概念与进程adj级别

       手机越用越慢?鲁大师老化实验室告诉你真相

       【朝花夕拾】Android性能篇之(五)Android虚拟机

网友评论

登录后评论
0/500
评论
宋者为王
+ 关注