Android对Linux系统的内存管理机制进行的优化


由于你的分享、点赞、在看
我足足的精气神儿!

Android对内存的使用方式一样是“尽最大限度的使用”,这一点继承了Linux的优势。只不过有所不一样的是,Linux侧重于尽量多的缓存磁盘数据以下降磁盘IO进而提升系统的数据访问性能,而 Android侧重于尽量多的缓存进程以提升应用启动和切换速度。Linux系统在进程活动中止后就结束该进程,而Android系统则会在内存中尽可能长时间的保持应用进程,直到系统须要更多内存为止 。这些保留在内存中的进程,一般状况下不会影响系统总体运行速度,反而会在用户再次激活这些进程时,加快进程的启动速度,由于不用从新加载界面资源了,这是Android标榜的特性之一。因此,Android如今不推荐显式的“退出”应用。web

那为何内存少的时候运行大型程序会慢呢,缘由是:在内存剩余很少时打开大型程序会触发系统自身的进程调度策略,这是十分消耗系统资源的操做,特别是在一个程序频繁向系统申请内存的时候。这种状况下系统并不会关闭全部打开的进程,而是选择性关闭,频繁的调度天然会拖慢系统。算法

Android中的进程管理

说到Android的内存管理,就不得不提到进程管理,由于进程管理确确切切的影响着系统内存。在了解进程管理以前,咱们首先了解一些基础概念。设计模式

当某个应用组件启动且该应用没有运行其余任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认状况下,同一应用的全部组件在相同的进程和线程(称为“主”线程) 中运行。若是某个应用组件启动且该应用已存在进程(由于存在该应用的其余组件),则该组件会在此进程内启动并使用相同的执行线程。可是,你也能够安排应用中的其余组件在单独的进程中运行,并为任何进程建立额外的线程。缓存

Android应用模型的设计思想取自Web 2.0的Mashup概念,是 基于组件的应用设计模式。在该模型下,每一个应用都由一系列的组件搭建而成,组件经过应用的配置文件描述功能。Android系统依照组件的配置信息,了解各个组件的功能并进行统一调度。这就意味着,来自不一样应用的组件能够有机地结合在一块儿,共同完成任务,各个Android应用,只有明确的组件边界,而再也不有明确的进程边界和应用边界。这种设计,也令得开发者无需耗费精力去从新开发一些附属功能,而是能够全身心地投入到核心功能的开发中。这样不但提升了应用开发的效率,也加强了用户体验(好比电子邮件中选择图片做为附件的功能,能够直接调用专门的图片应用的功能,不用本身从头开发)。微信


系统不会为每一个组件实例建立单独的线程。运行于同一进程的全部组件均在 UI 线程中实例化,而且对每一个组件的系统调用均由该线程进行分派。 所以,响应系统回调的方法(例如,报告用户操做的 onKeyDown() 或生命周期回调方法)始终在进程的 UI 线程中运行(四大组件的各个生命周期回调方法都是在UI线程中触发的)。网络

进程的生命周期

Android的一个不寻常的基本特征是应用程序进程的生命周期并不是是由应用自己直接控制的。相反,进程的生命周期是由系统决定的,系统会权衡每一个进程对用户的相对重要程度,以及系统的可用内存总量来肯定。 好比说相对于终止一个托管了正在与用户交互的Activity的进程,系统更可能终止一个托管了屏幕上再也不可见的Activity的进程,不然这种后果是可怕的。所以,是否终止某个进程取决于该进程中所运行组件的状态 。Android会有限清理那些已经再也不使用的进程,以保证最小的反作用。app

做为应用开发者,了解各个应用组件(特别是Activity、Service和BroadcastReceiver)如何影响应用进程的生命周期很是重要。不正确的使用这些组件,有可能致使系统在应用执行重要工做时终止进程。编辑器

举个常见的例子, BroadcastReceiver 在其 onReceive() 方法中接收到Intent时启动一个线程,而后从该函数返回。而一旦返回,系统就认为该 BroadcastReceiver 再也不处于活动状态,所以也就再也不须要其托管进程(除非该进程中还有其余组件处于活动状态)。这样一来,系统就有可能随时终止进程以回收内存,而这也最终会致使运行在进程中的线程被终止。此问题的解决方案一般是从 BroadcastReceiver 中安排一个 JobService ,以便系统知道在该进程中仍有活动的工做。ide

为了肯定在内存不足时终止哪些进程,Android会根据进程中正在运行的组件以及这些组件的状态,将每一个进程放入 “重要性层次结构” 中。必要时,系统会首先杀死重要性最低的进程,以此类推,以回收系统资源。这就至关于为进程分配了优先级的概念。函数

进程优先级

Android中总共有5个进程优先级(按重要性降序):

Foreground Process:前台进程(正常不会被杀死)

用户当前操做所必需的进程。有不少组件能以不一样的方式使得其所在进程被断定为前台进程。若是一个进程知足如下任一条件,即视为前台进程:

  • 托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)

  • 托管某个 Service,后者绑定到用户正在交互的 Activity

  • 托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())

  • 托管正执行其 onReceive() 方法的 BroadcastReceiver

一般,在任意给定时间前台进程都为数很少。只有在内存不足以支持它们同时继续运行这一万不得已的状况下,系统才会终止它们。此时,设备每每已达到内存分页状态,所以须要终止一些前台进程来确保用户界面正常响应。

Visible Process:可见进程(正常不会被杀死

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。杀死这类进程也会明显影响用户体验。若是一个进程知足如下任一条件,即视为可见进程:

  • 托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,启动了一个对话框样式的前台 activity ,此时在其后面仍然能够看到前一个Activity。

运行时权限对话框就属于此类。考虑一下,还有哪一种状况会致使只触发onPause而不触发onStop?

  • 托管经过 Service.startForeground() 启动的前台Service。

Service.startForeground():它要求系统将它视为用户可察觉到的服务,或者基本上对用户是可见的。

  • 托管系统用于某个用户可察觉的特定功能的Service,好比动态壁纸、输入法服务等等。

可见进程被视为是极其重要的进程,除非为了维持全部前台进程同时运行而必须终止,不然系统不会终止这些进程。若是这类进程被杀死,从用户的角度看,这意味着当前 activity 背后的可见 activity 会被黑屏代替。

Service Process:服务进程(正常不会被杀死)

正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,可是它们一般在执行一些用户关心的操做(例如,后台网络上传或下载数据)。所以,除非内存不足以维持全部前台进程和可见进程同时运行,不然系统会让服务进程保持运行状态。

已经运行好久(例如30分钟或更久)的Service,有可能被降级,这样一来它们所在的进程就能够被放入Cached LRU列表中。这有助于避免一些长时间运行的Service因为内存泄漏或其余问题而消耗过多的RAM,进而致使系统没法有效使用缓存进程的状况。

Background / Cached Process:后台进程(可能随时被杀死)

这类进程通常会持有一个或多个目前对用户不可见的 Activity (已调用 Activity 的 onStop() 方法)。它们不是当前所必须的,所以当其余更高优先级的进程须要内存时,系统可能 随时终止 它们以回收内存。但若是正确实现了Activity的生命周期,即使系统终止了进程,当用户再次返回应用时也不会影响用户体验:关联Activity在新的进程中被从新建立时能够恢复以前保存的状态。

在一个正常运行的系统中,缓存进程是内存管理中 惟一 涉及到的进程:一个运行良好的系统将始终具备多个缓存进程(为了更高效的切换应用),并根据须要按期终止最旧的进程。只有在很是严重(而且不可取)的状况下,系统才会到达这样一个点,此时全部的缓存进程都已被终止,而且必须开始终止服务进程。

Android系统回收后台进程的参考条件

  • LRU算法:自下而上开始终止,先回收最老的进程。越老的进程近期内被用户再次使用的概率越低。杀死的进程越老,对用户体验的影响就越小。

  • 回收收益:系统老是倾向于杀死一个能回收更多内存的进程,由于在它被杀时会为系统提供更多内存增益,从而能够杀死更少的进程。杀死的进程越少,对用户体验的影响就越小。换句话说,应用进程在整个LRU列表中消耗的内存越少,保留在列表中而且可以快速恢复的机会就越大。

这类进程会被保存在一个伪LRU列表中,系统会优先杀死处于列表尾部(最老)的进程,以确保包含用户最近查看的 Activity 的进程最后一个被终止。这个LRU列表排序的确切策略是平台的实现细节,但一般状况下,相对于其余类型的进程,系统会优先尝试保留更有用的进程(好比托管用户主应用程序的进程,或者托管用户看到的最后一个Activity的进程,等等)。还有其余一些用于终止进程的策略:对容许的进程数量硬限制,对进程能够持续缓存的时间量的硬限制,等等。

在一个健康的系统中,只有缓存进程或者空进程会被系统随时终止,若是服务进程,或者更高优先级的可见进程以及前台进程也开始被系统终止(不包括应用自己糟糕的内存使用致使OOM),那就说明系统运行已经处于一个亚健康甚至极不健康的状态,可用内存已经吃紧。

Empty Process:空进程(能够随时杀死)

不含任何活跃组件的进程。保留这种进程的的惟一目的是用做缓存(为了更加有效的使用内存而不是彻底释放掉),以缩短下次启动应用程序所需的时间,由于启动一个新的进程也是须要代价的。只要有须要,Android会随时杀死这些进程。

内存管理中对于前台/后台应用的定义,与用于Service限制目的的后台应用定义不一样。从Android 8.0开始,出于节省系统资源、优化用户体验、提升电池续航能力的考量,系统进行了前台/后台应用的区分,对于后台service进行了一些限制。在该定义中,若是知足如下任意条件,应用将被视为处于前台:

>

  • 具备可见 Activity(无论该 Activity 已启动仍是已暂停)。

  • 具备前台 Service。

  • 另外一个前台应用已关联到该应用(无论是经过绑定到其中一个 Service,仍是经过使用其中一个内容提供程序)。例如,若是另外一个应用绑定到该应用的 Service,那么该应用处于前台:IME 壁纸 Service 通知侦听器 语音或文本 Service 若是以上条件均不知足,应用将被视为处于后台。

Android系统如何评定进程的优先级

根据进程中当前活动组件的重要程度,Android 会将进程评定为它可能达到的最高级别。 例如,若是某进程同时托管着 Service 和可见 Activity,则会将此进程评定为可见进程,而不是服务进程。

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

因为运行服务的进程其级别高于托管后台 Activity 的进程,所以,在 Activity 中启动一个长时间运行的操做时,最好为该操做启动服务,而不是简单地建立工做线程,当操做有可能比 Activity 更加持久时尤要如此。例如,一个文件上传的操做就能够考虑使用服务来完成,这样一来,即便用户退出 Activity,仍可在后台继续执行上传操做。使用服务能够保证,不管 Activity 发生什么状况,该操做至少具有“服务进程”优先级。同理, BroadcastReceiver 也应使用服务,而不是简单地将耗时冗长的操做放入线程中。

Home键退出和返回键退出的区别

Home键退出,程序保留状态为后台进程;而返回键退出,程序保留状态为空进程,空进程更容易被系统回收。Home键其实主要用于进程间切换,返回键则是真正的退出程序。

从理论上来说,不管是哪一种状况,在没有任何后台工做线程(即使应用处于后台,工做线程仍然能够执行)的前提下,被置于后台的进程都只是保留他们的运行状态,并不会占用CPU资源,因此也不耗电。只有音乐播放软件之类的应用须要在后台运行Service,而Service是须要占用CPU时间的,此时才会耗电。因此说没有带后台服务的应用是不耗电也不占用CPU时间的,不必关闭,这种设计自己就是Android的优点之一,可让应用下次启动时更快。然而现实是,不少应用多多少少都会有一些后台工做线程,这多是开发人员经验不足致使(好比线程未关闭或者循环发送的Handler消息未中止),也多是为了需求而有意为之,致使整个Android应用的生态环境并非一片干净。




夯实基础,关注前沿,娱乐生活

掌握更多前沿技术,获取更多笑点 

请关注--------喘口仙氣



本文分享自微信公众号 - 喘口仙氣(gh_db8538619cdd)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索