前言的前言
你好,我是 Flywith24
已经快两个月没在掘金上发文了,熟悉我的小伙伴可能知道,我经常会将文章分门别类,按照系列来写。
这是因为我一直比较认同两个学习理念:
- 输入倒逼输出
- 建立系统化的知识体系
也是基于这两个理念,在 2018 年,大学刚毕业的我使用一个月的工资购买了 扔物线 的 HenCoder Plus 系列课程。而在 19 底,我在订阅了 KunMinX 的 重学安卓 后,开始了自己的写作之路。
在今年的 8 月 24 日,我开启了一个新的系列:Android Detail,该专栏致力于帮助小伙伴们建立系统化的知识体系。就在昨天,专栏迎来了第 100 位订阅者。
唠叨结束,让我们开始这篇文章吧。
前言
很高兴见到你!👋
本文是进程篇的第二篇,前文 介绍了 Android 进程的一些核心概念,而本文将沿着两条线继续介绍进程相关的内容。
第一部分介绍 Android 中内存是如何分配的以及内存不足时的管理策略;第二部分介绍内存不足时清理内存的依据——进程优先级。
了解这些内容,再去看应用的生命周期,Activity 的生命周期等内容就会有不一样的理解。
阅读本文,你将了解:
- Android 是如何进行进程间的内存分配的
- 如何统计应用的内存占用
- 当内存不足时系统使用哪两种手段来释放内存
- 常用的进程类型
- 进程的优先级
- ADJ 与 procstate
- 通过操作 + 日志直观感受 ADJ 与 procstate 的变化
- 我就想知道进程是怎么没滴(范伟老师脸😆)
- 通过一个案例分析流氓软件的恶心行为
推荐阅读
以下内容与本文搭配阅读效果更佳。😉
-
👆 强烈建议阅读
谈谈 Android 对内存使用的设计理念
Activity 任务,返回栈 一文中我们曾讨论过 Android 多任务的设计理念。为了保持最好的用户体验,Android 被设计为可以同时执行多个任务,换句话说便是允许多个 App 同时运行并使其能够快速相互切换。
而从内存的角度来说,想要实现「丝滑」切换,则必须保证切换到该应用时其对应的进程已创建并加载至内存。理想状态下,我们希望所有应用都处于运行状态。但在软件工程中,「时间」和「空间」总是一对矛盾的存在,想要获得更短的「时间」(丝滑的使用体验),则必须付出更多「空间」(加大内存)。从另一方面讲,应用长时间保持运行状态会耗费更多的电量,导致设备续航能力变差,进而影响用户体验。
Android 内存管理就是在这种矛盾的背景下设计出来的。系统不会立即杀死使用完的进程,反而会对之前创建过的进程进行缓存。当设备内存紧张时,按照一定的策略回收内存。当设备内存低至一定阈值时,系统会按照策略杀死进程以达到释放内存的目的。
本文前半部分介绍 Android 内存管理的主要结构,内存不足时的管理策略;后半部分介绍系统是按照何种策略杀死进程的,有哪些杀进程的方法。
进程间的内存分配
内存类型
Android 设备包含三种不同类型的内存:RAM、zRAM 和存储器。
- RAM 是最快的内存类型,但其大小通常有限。高端设备通常具有最大的 RAM 容量
- zRAM 是用于交换空间的 RAM 分区。所有数据在放入 zRAM 时都会进行压缩,然后在从 zRAM 向外复制时进行解压。这部分 RAM 会随着页面进出 zRAM 而增大或缩小。设备制造商可以设置 zRAM 大小上限
- 存储器中包含所有持久性数据(例如文件系统等),以及为所有应用、库和平台添加的代码。存储器比另外两种内存的容量大得多。在 Android 上,存储器不像在其他 Linux 实现上那样用于交换空间,因为频繁写入会导致这种内存出现损坏,并缩短存储媒介的使用寿命
内存页
Android 的物理内存被分为多个「页」(page)。通常,每个页拥有 4KB 的内存。
不同类型的页有着各自的作用:
-
已用页(Used Pages)
被进程活跃使用的内存页
-
缓存页(Cached Pages)
进程正在使用的内存页,缓存页在存储器中有相应的备份,必要时可以回收
-
空闲页(Free Pages)
未使用的内存
其中 缓存页 又分为 私有页 和 共享页,它们各自又分 干净页 脏页:
- 私有页:由一个进程拥有且未共享
-
干净页:存储器中未经修改的文件备份
-
脏页:存储器中经过修改的文件备份
-
- 共享页:由多个进程使用
- 干净页:存储器中未经修改的文件备份
- 脏页:存储器中经过修改的文件备份
🌟 注意:干净页包含存在于存储器中的文件(或文件一部分)的精确备份。如果干净页不再包含文件的精确备份(例如,因应用操作所致),则会变成脏页。干净页可以删除,因为始终可以使用存储器中的数据重新生成它们;脏页则不能删除,否则数据将会丢失。
统计内存占用
如何知道应用程序占用的内存呢?
前文我们提到,设备的内存分页管理。Linux 内核会追踪设备上运行的每个进程正在使用的页。
统计应用程序的内存占用,我们只需计算出应用正在使用的页数即可。这个过程略微复杂,因为还要考虑共享页的情况。使用相同服务或