Android弹药库——内存管理机制与进程模型

4,311 阅读25分钟
题外话:喜欢文章的朋友点个赞鼓励鼓励呗~

本篇文章是对 Android 内存管理、进程管理的简单总结,主要偏理论性,但是了解个中原理,对 Android 系统的认知、对高质量程序的编写、对程序的理解都大有裨益~

Android内存管理

Android的内存管理哲学

Android 是基于 Linux 内核实现的操作系统,而 Linux 的内存管理哲学是:Free memory is wasted memory,即内存没得到充分利用就是在浪费资源。

Linux 希望尽可能多的使用内存,减少磁盘 IO,因为内存的速度比磁盘快得多。Linux 总是在力求缓存更多的数据和信息,内存不够时,将一些不经常使用的数据转移到交换分区(Swap Space)中以释放更多可用物理内存,当然,如果交换分区的数据再次被读写时,又会被转移到物理内存中,这种设计思路提高了系统的整体性能。

Linux 和 Windows 在内存管理机制上的区别
在 Linux 系统使用过程中,你会发现,无论你的电脑内存配置多么优越,仍然不时的发生可用内存吃紧的现象,感觉内存不够用了,其实不然。这是 Linux 内存管理的优秀特性,无论物理内存有多大, Linux 都将其充分利用,将一些程序调用过的硬盘数据缓存到内存,利用内存读写的高速性提高系统的数据访问性能。而 Windows 只在需要内存时,才为应用分配内存,不能充分利用大容量的内存空间。换句话说,每增加一些内存,Linux 都能将其利用起来,充分发挥硬件投资带来的好处,而 Windows 只将其作为摆设。

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

那为什么内存少的时候运行大型程序会慢呢,原因是:在内存剩余不多时打开大型程序会触发系统自身的进程调度策略,这是十分消耗系统资源的操作,特别是在一个程序频繁向系统申请内存的时候。这种情况下系统并不会关闭所有打开的进程,而是选择性关闭,频繁的调度自然会拖慢系统。

一句话概括就是,在占用大量内存情况下,缓存提高性能。我们开发者在日常的 app 开发工作中,也大量使用到了缓存机制提高应用的性能,提高用户的体验。

例如经典的三级缓存,先从内存中找,内存找不到从磁盘上找,磁盘上找不到就只能走网络请求了;

又例如为什么有的应用要做保活?一个重要的原因,保活能够使得应用进程缓存在 Android 系统中不被清理,从而达到应用 启动优化(温馨提示:可点击哟~) 的效果。但要注意的是,一个空的进程也会占用 10MB 的内存,而有些应用启动就有十几个进程,甚至有些应用已经从双进程保活升级到四进程保活,这就过分了,此时应该减少应用启动的进程数、减少常驻进程、有节操的保活,这对低端机内存优化非常重要;

再例如开发工作中常用的 Glide 图片加载框架、OkHttp 网络请求框架等第三方框架,它们的源码设计构架无不是建立在基于优秀缓存机制之上(后续的文章,我会对一些常用第三方框架源码进行分析)......

Android对应用进程内存的分配和回收

Android 为每个进程分配内存的时候,采用了弹性分配方式,即刚开始并不会分配很多的内存给到每个进程,而是给每一个进程分配一个“够用”的虚拟内存范围。这个范围是根据每一个设备实际的物理内存大小来决定的,并且可以随着应用后续需求而增加,但最多也只能达到系统为每个应用定义的上限。

Android 会在内存中尽量长时间的保持应用进程,即使有些进程不再使用了。这样,当用户下次启动应用的时候,只需要恢复当前进程就可以了,不需要重新创建进程,进而达到了减少应用的启动时间,提升用户体验的效果。但是一当 Android 系统发现内存不足,而其他为用户提供更紧急服务的进程又需要内存的时候,Android 就会决定关闭某些进程以回收内存。这部分就涉及到 Android 的幽灵刽子手 LMK 低内存管理机制,low memory killer,它是基于Linux内核的 OOM Killer 机制诞生。

说到这里,又不得不提一下 oom_score_adj(在 Android 7.0 系统以前是 oom_adj),什么是 oom_score_adj?它是 Linux 内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于 oom_score_adj 的作用,在这里,我们只需要了解以下几点即可:

  • 进程的 oom_score_adj 越大,表示此进程优先级越低,越容易被杀死回收;反之,越小,表示进程优先级越高,越不容易被杀死回收
  • 普通app进程的 oom_score_adj >= 0,系统进程的 oom_score_adj 才可能 < 0
  • 进程的 oom_score_adj 的值不是一成不变的,Android 系统会根据应用是否在前台等情况改变该值
疑问   oom_score_adj 的取值范围是多少,以及oom_score_adj 的每个值代表什么意思呢?系统会在什么时候更新 oom_score_adj (即进程优先级),又会在什么时候根据 oom_score_adj 的值去选择性杀进程呢?应用进程怎样提高 oom_score_adj 的值,从而能够获取更高的进程优先级不被杀死呢?关于这些问题,我们留在下一节“Android进程”中讨论。

Android对应用内存的限制

为了维护高效的多任务环境,Android 为每个应用程序设置了堆大小 DalvikHeapSize 最大限制阈值的硬性限制。 该限制因设备而异,取决于设备总体可用的 RAM。 如果应用程序已达到该限制并尝试分配更多内存,就会很容易引发  OutOfMemoryError,即 OOM crash。

提示  在某些情况下,你可能希望查询系统以准确确定当前设备上可用的堆空间大小,例如,确定可以安全地保留在缓存中的数据量。ActivityManager.getMemoryClass() 可以用来查询当前应用的HeapSize阈值,这个方法会返回一个整数,表明应用的HeapSize阈值是多少MB,指示应用程序堆可用的兆数。

Android对应用进程的切换

当用户在应用程序之间切换时,Android 会将非前台应用程序(即用户不可见或并没有运行诸如音乐播放等前台服务的进程)缓存到一个最近最少使用缓存(LRU Cache)中。例如,当用户首次启动应用程序时,会为其创建一个进程; 但是当用户离开应用程序时,该进程不会退出。 系统会缓存该进程。 如果用户稍后返回应用程序,系统将重新使用该进程,从而使应用程序切换更快。

如果你的应用程序具有缓存进程并且它保留了当前不需要的内存,那么即使用户未使用它,你的应用程序也会影响系统的整体性能。 当系统内存不足时,就会从最近最少使用的进程开始,终止 LRU Cache 中的进程。另外,系统还会综合考虑保留了最多内存的进程,并可能终止它们以释放 RAM。一句话概括,在进程不再前台的时候,释放该释放的应用缓存,因为当系统内存不足时,这些未释放的缓存有可能成为“压倒大象的最后一根稻草”。

当系统开始终止 LRU Cache 中的进程时,它主要是自下而上的。 系统还会考虑哪些进程占用更多内存,因为在它被杀时会为系统提供更多内存增益。 因此在整个 LRU 列表中消耗的内存越少,保留在列表中并且能够快速恢复的机会就越大。

Android垃圾回收机制

Android 系统根据所分配对象的类型以及 GC 时系统如何管理这些对象,将进程所使用的内存分成了多个空间。新分配的对象位于什么空间,取决于你的 Android Runtime 是什么版本,5.0 以上系统采用的是 ART(Android Runtime)虚拟机,5.0以下采用的是 Dalvik 虚拟机。

无论是 ART 还是 Dalvik 虚拟机,都和众多 Java 虚拟机一样,属于一种托管内存环境(程序员不需要显示的管理内存的分配与回收,交由系统自动管理)。托管内存环境会跟踪每个内存分配, 一旦确定程序不再使用一块内存,它就会将其释放回堆中,而无需程序员的任何干预。回收托管内存环境中已经分配但不可达对象内存的机制称为垃圾回收,简称 GC 。GC 通过确定对象是否被活动对象引用来确定是否收集该对象,进而动态回收无任何引用的对象占据的内存空间。

每个空间都有大小限制,系统会跟踪整个程序所占用的内存大小。当程序占用内存达到一定程度时,系统就会触发 GC 回收内存,以便将来分配给其它对象:

                    

ART 虚拟机相较于 Dalvik 在 GC 的性能上有较大的提升。

  • Dalvik 虚拟机在 GC 的整个流程中,绝大部分时间会“Stop the world”,即暂停所有线程(包括负责渲染界面的主线程)的执行,只执行 GC 线程,直至 GC 完成,这就会导致一个问题“当 GC 过于频繁时,阻塞了主线程,导致丢帧(Android 系统每 16ms 渲染一帧),最终导致界面的卡顿”:

                 

  • ART 虚拟机则在 Dalvik 虚拟机阻塞性 GC 的基础上,扩展了并发 GC 算法,消除了大面积的 GC 阻塞时间(当然也不是说全程不阻塞,在某一小段时间内还是会出现“Stop the world”),提升了 GC 的性能,也在一定的程度上优化了卡顿情况:

                         


Android 的内存堆是分代式(Generational)的,意味着它会将所有分配的对象进行分代,然后分代跟踪这些对象。 例如,最近分配的对象属于年轻代(Young Generation)。 当一个对象长时间保持活动状态时,它可以被提升为年老代(Older Generation),之后还能进一步提升为持久代(Permanent Generation)。

                                

  • Young Generation(年轻代):Faster but more frequent,垃圾回收效率高且频繁

年轻代分为三个区,一个 eden 区,另外的两个 S0 和 S1 都是 Survivor 区(S0 和 S1 只是为了说明,两者实质上一样,方向可互换)。程序中生成的大部分新的对象都在 eden 区中,当 eden 区满时,还存活的对象将被复制到其中一个 Survivor 区,当此 Survivor 区的对象占用空间满时,此区存活的对象又被复制到另外一个 Survivor 区,当这个 Survivor 区也满时,从第一个 Survivor 区复制过来的并且此时还存活的对象,将被复制到年老代。

  • Old Generation(年老代):Slower but less frequent,垃圾回收速度慢且相对年轻代频率较低

年老代存放的是上面年轻代复制过来的对象,也就是在年轻代中还存活的对象,并且区满了复制过来的。一般来说,年老代中的对象生命周期都比较长。

  • Permanent Generation(持久代)

用于存放静态的类和方法,垃圾回收对持久代没有显著影响。

在三级 Generation 内存模型中,每一个区域的大小都是有固定值的,当进入的对象总大小到达某一级内存区域阀值的时候就会触发 GC 机制,进行垃圾回收,腾出空间以便其他对象进入。                                                            

读到这里,我们可以总结一下一个对象从被分配开始,在三级内存模型中随时间的迁移的变化流程:

  1. 对象刚创建的时候,存放于 eden 区
  2. 当执行 GC 是,如果对象仍然存活,则复制到 S0 区
  3. 当 S0 区满时,该区存活对象将复制到 S1 区,然后清空 S0 区,接下来 S0 和 S1 角色互换
  4. 当步骤3达到一定次数(系统版本不同会有差异)后,存活对象将被复制到 Old Generation
  5. 当这个对象在 Old Generation 区域停留累积一定时间后,会被移动到PermanentGeneration区域。PermanentGeneration区域也存放一些静态文件,如Java类等。

                             


现在,我们已经知道了 GC 机制的内存分代模型,以及不同代区域的作用,那么为什么需要对堆进行分代呢?不分代不能完成他所做的事情么?最简单的,为什么将年轻代以及年老代合并成一个呢,而非要区分开呢?其实不分代完全可以,分代的唯一理由就是优化 GC 性能。我们这么想想,如果没有分代,那所有的对象都在同一个区域,当 GC 的时候我们要找到哪些对象需要被回收,这就需要对堆的所有区域进行扫描,这样子会比较耗时。而我们的大部分对象的生命周期很短暂的,正所谓“朝生夕死”,如果采用分代的策略,我们把新创建的对象放到某一代,当 GC 的时候先把这块存“朝生夕死”对象的区域进行回收,这样就会高效率的腾出很大的空间出来。同时,不同代之间可以使用不同的垃圾回收算法:

  • 年轻代使用 "标记-复制" 法进行 GC
  • 老年代使用 "标记-整理" 法进行 GC
在这里就不详细阐述算法了,想了解 GC 算法参考以下两篇文章:
https://juejin.cn/post/6844903749782077448
https://www.cnblogs.com/fangfuhai/p/7203468.html


Android进程

进程生命周期

Android 系统会尽量长时间地帮助我们缓存应用的进程,已达到快速启动的效果,但是当内存吃急的时候,系统依旧会将相对不重要的进程从缓存中移除,达到内存释放的作用。Android 系统一个基本特征就是应用程序进程的生命周期并不是由应用本身直接控制的,而是由系统决定的,系统会权衡每个进程对用户的相对重要程度,以及系统的可用内存总量来确定。比如说相对于终止一个正在与用户交互的 Activity 的进程,系统更可能终止一个在屏幕上不再可见的 Activity 的进程,否则这种后果将会是可怕的,将会给用户带来极其不好的用户体验。因此,是否终止某个进程取决于该进程中所运行组件的状态。Android 会有限清理那些已经不再使用的进程,以保证最小的副作用。

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

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

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

进程优先级

上面关于 oom_score_adj 进程优先级留下了几个疑问,现在,我们一一地解决:

第一个疑问,oom_score_adj 的取值范围是多少,以及 oom_score_adj 的每个值代表什么意思呢?

对于每一个运行中的进程,Linux 内核都通过 proc文件系统 暴露这样一个文件来允许其他程序修改指定进程的优先级:

/proc/[pid]/oom_score_adj。(修改这个文件需要 root 权限)

这个文件允许的值的范围是:-1000 ~ +1000之间。值越小,表示进程越重要

当内存非常紧张时,系统便会遍历所有进程,以确定哪个进程需要被杀死以回收内存,此时便会读取 oom_score_adj 这个文件的值。关于这个值的使用,在后面讲解进程回收的的时候,我们会详细讲解。

提示:在 Linux 2.6.36 之前的版本中,Linux 提供调整优先级的文件是/proc/[pid]/oom_adj。这个文件允许的值的范围是-17 ~ +15之间。数值越小表示进程越重要。 这个文件在新版的 Linux 中已经废弃。

但你仍然可以使用这个文件,当你修改这个文件的时候,内核会直接进行换算,将结果反映到 oom_score_adj 这个文件上。

换算公式:oom_score_adj = oom_adj * 1000 / 17

Android 系统是基于 Linux 系统的,因此在早期的 Android 版本实现中也是依赖 oom_adj 这个文件。但是在 Android 7.0 版本开始,已经切换到使用 oom_score_adj 这个文件,优先级的取值由原本的-17~+15变为了-1000~+1000,这样的调整可以更进一步地细化进程的优先级,比如在 VISIBLE_APP_ADJ(100)PERCEPTIBLE_APP_ADJ(200) 之间,可以有adj =101、102级别的进程。

为了便于管理,ProcessList.java 中预定义了 oom_score_adj 的可能取值。

其实这里的预定义值也是对应用进程的一种分类,它们是:

ADJ级别取值含义
NATIVE_ADJ-1000native进程
SYSTEM_ADJ-900仅指system_server进程
PERSISTENT_PROC_ADJ-800系统persistent进程
PERSISTENT_SERVICE_ADJ-700关联着系统或persistent进程
FOREGROUND_APP_ADJ0前台进程
VISIBLE_APP_ADJ100可见进程
PERCEPTIBLE_APP_ADJ200可感知进程,比如后台音乐播放
BACKUP_APP_ADJ300备份进程
HEAVY_WEIGHT_APP_ADJ400重量级进程
SERVICE_ADJ500服务进程
HOME_APP_ADJ600Home进程
PREVIOUS_APP_ADJ700上一个进程
SERVICE_B_ADJ800B List中的Service
CACHED_APP_MIN_ADJ900不可见进程的adj最小值
CACHED_APP_MAX_ADJ906不可见进程的adj最大值

提示
   同一个进程,在不同状态下,其 oom_score_adj 值是不一样的,也就是说进程的优先级不是一成不变的。从表中,我们可以清晰地看出每个 oom_score_adj 值的含义,以及进程应具备什么样的特征才能够获取相对应的优先级。

由上面的表,我们可以看出,FOREGROUND_APP_ADJ = 0,这个是前台应用进程的优先级,这是用户正在交互的应用,它们是很重要的,系统不应当把它们回收了,这个也是普通应用程序的最高优先级了,小于0的优先级都是由系统进程持有。


第二个疑问,系统会在什么时候更新 oom_score_adj(即进程优先级),又会在什么时候根据 oom_score_adj 的值去选择性杀进程呢(关于这个疑问,在这里只纸上得来终觉浅,要想深入了解,还是要进到 Android 源码的世界中仔细研读~)?

系统什么时候更新 oom_score_adj ?

系统会对处于不同状态的进程设置不同的优先级。但实际上,进程的状态是一直在变化中的。例如:用户可以随时会启动一个新的 Activity,或者将一个前台的 Activity 切换到后台。在这个时候,发生状态变化的 Activity 的所在进程的优先级就需要进行更新。

并且,Activity 可能会使用其他的 Service 或者 ContentProvider。当 Activity 的进程优先级发生变化的时候,它所使用的 Service 或者 ContentProvider 的优先级也应当发生变化。

ActivityManagerService 中有如下两个方法用来更新进程的优先级:

  • final boolean updateOomAdjLocked(ProcessRecord app)
  • final void updateOomAdjLocked()

第一个方法是针对指定的单个进程更新优先级。第二个是对所有进程更新优先级。

在下面的这些情况下,需要对指定的应用进程更新优先级:

  • 当有一个新的进程开始使用本进程中的 ContentProvider
  • 当本进程中的一个 Service 被其他进程 bind 或者 unbind
  • 当本进程中的 Service 的执行完成或者退出了
  • 当本进程中一个 BroadcastReceiver 正在接受广播
  • 当本进程中的 BackUpAgent 启动或者退出了

在有些情况下,系统需要对所有应用进程的优先级进行更新,譬如:

  • 当有一个新的进程启动时
  • 当有一个进程退出时
  • 当系统在清理后台进程时
  • 当有一个进程被标记为前台进程时
  • 当有一个进程进入或者退出cached状态时
  • 当系统锁屏或者解锁时
  • 当有一个 Activity 启动或者退出时
  • 当系统正在处理一个广播事件时
  • 当前台 Activity 发生改变时
  • 当有一个 Service 启动时

系统什么时候根据 oom_score_adj 杀进程?

在 ActivityManagerService 调用updateOomAdjLocked()时,会判断进程是否需要被杀死,若是,则调用ProceeRecord::kill()方法杀死该进程:

默认限制 empty 或 cached 进程的上限为 16 个,并且 empty 超过 8 个时会清理掉 30 分钟没有活跃的进程。 cached 和 empty 主要是区别是否有 Activity。


第三个疑问,应用进程怎样降低 oom_score_adj 值,从而能够获取更高的进程优先级不被杀死呢?

在 Android 系统中,通常按照优先级将进程分为五个等级(优先级从高往低排),LMK(Low Memory Killer) 杀死进程时,将遵从 空进程 -> 后台进程 -> 服务进程 -> 可见进程 -> 前台进程 的顺序,也就是说,要想降低 oom_score_adj 值,降低进程被 LMK 杀死的机率,我们可以控制进程在服务进程 -> 可见进程 -> 前台进程上,越接近前台进程的级别,越不会被杀死:

                 

  • 前台进程(foreground process,正常不会被杀死)

前台进程是用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

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

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

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

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

通常,系统中只会有少量几个前台进程的存在,前台进程是最高级的进程,只有在系统内存极其不足,甚至系统剩余内存都不足以让这些前台进程正常运行的情况下,系统才会杀死他们,回收内存空间。在这种情况下,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

  • 可见进程(visible process,正常不会被杀死

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

  1. 托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,启动了一个对话框样式的前台 activity ,此时在其后面仍然可以看到前一个Activity。(运行时权限对话框就属于此类。考虑一下,还有哪种情况会导致只触发onPause而不触发onStop?)
  2. 托管通过 Service.startForeground() 启动的前台Service。(Service.startForeground():它要求系统将它视为用户可察觉到的服务,或者基本上对用户是可见的。)
  3. 托管系统用于某个用户可察觉的特定功能的Service,比如动态壁纸、输入法服务等等。

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

  • 服务进程(service process,正常不会被杀死

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

  • 后台进程(Background / Cached Process,随时被杀死

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

  • 空进程(Empty Process,随时被杀死

不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

总结

到此,我们对 Android 的内存管理机制,以及进程模型都进行了概述性的分析,也许有的读者会觉得“Android 应用开发根本用不上这些东西,学来有什么用”,但是真的是这样吗?

至少我不这么认为,了解了这些系统运行机制,我们会对 Android 系统有更深的理解,对系统的内存机制,进程机制有一定的认知,对我们书写高质量代码,日常攻克难题,整体架构思想以及性能优化工作都有很大的帮助。

在这篇文章,我们学到了什么?最主要的有以下几点:

  • Android 内存管理的哲学——Free memory is wasted memory,很大程度上,我们日常开发工作的的缓存机制,进程保活机制(有节操的保活~)就是对这句话的很好缩影
  • 堆分代模型、GC 垃圾回收机制概述
  • 进程的优先级:空进程 -> 后台进程 -> 服务进程 -> 可见进程 -> 前台进程,从左到右,优先级越来越高,系统倾向于杀死低优先级的进程,所以,进程的保活可以从提高进程优先级做起
  • Android 系统进程管理的简单概述

最后,本人水平有限,不免会有错误,如有错误,请多多指教~~~

题外话:喜欢文章的朋友点个赞鼓励鼓励呗~
参考资料:
罗彧成老师书籍《Android应用性能优化最佳时间》
https://developer.android.google.cn/guide/components/processes-and-threads#Processes
https://developer.android.google.cn/guide/components/activities/process-lifecycle
http://gityuan.com/2015/10/01/process-lifecycle/
https://juejin.cn/post/6844903749782077448
https://paul.pub/android-process-priority/
https://gityuan.com/2018/05/19/android-process-adj/